visNetwork
// If window.HTMLWidgets is already defined, then use it; otherwise create a
// new object. This allows preceding code to set options that affect the
// initialization process (though none currently exist).
window.HTMLWidgets = window.HTMLWidgets || {};
// See if we're running in a viewer pane. If not, we're in a web browser.
var viewerMode = window.HTMLWidgets.viewerMode =
/\bviewer_pane=1\b/.test(window.location);
// See if we're running in Shiny mode. If not, it's a static document.
// Note that static widgets can appear in both Shiny and static modes, but
// obviously, Shiny widgets can only appear in Shiny apps/documents.
var shinyMode = window.HTMLWidgets.shinyMode =
typeof(window.Shiny) !== "undefined" && !!window.Shiny.outputBindings;
// We can't count on jQuery being available, so we implement our own
// version if necessary.
function querySelectorAll(scope, selector) {
if (typeof(jQuery) !== "undefined" && scope instanceof jQuery) {
return scope.find(selector);
}
if (scope.querySelectorAll) {
return scope.querySelectorAll(selector);
}
}
function asArray(value) {
if (value === null)
return [];
if ($.isArray(value))
return value;
return [value];
}
// Implement jQuery's extend
function extend(target /*, ... */) {
if (arguments.length == 1) {
return target;
}
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var prop in source) {
if (source.hasOwnProperty(prop)) {
target[prop] = source[prop];
}
}
}
return target;
}
// IE8 doesn't support Array.forEach.
function forEach(values, callback, thisArg) {
if (values.forEach) {
values.forEach(callback, thisArg);
} else {
for (var i = 0; i < values.length; i++) {
callback.call(thisArg, values[i], i, values);
}
}
}
// Replaces the specified method with the return value of funcSource.
//
// Note that funcSource should not BE the new method, it should be a function
// that RETURNS the new method. funcSource receives a single argument that is
// the overridden method, it can be called from the new method. The overridden
// method can be called like a regular function, it has the target permanently
// bound to it so "this" will work correctly.
function overrideMethod(target, methodName, funcSource) {
var superFunc = target[methodName] || function() {};
var superFuncBound = function() {
return superFunc.apply(target, arguments);
};
target[methodName] = funcSource(superFuncBound);
}
// Add a method to delegator that, when invoked, calls
// delegatee.methodName. If there is no such method on
// the delegatee, but there was one on delegator before
// delegateMethod was called, then the original version
// is invoked instead.
// For example:
//
// var a = {
// method1: function() { console.log('a1'); }
// method2: function() { console.log('a2'); }
// };
// var b = {
// method1: function() { console.log('b1'); }
// };
// delegateMethod(a, b, "method1");
// delegateMethod(a, b, "method2");
// a.method1();
// a.method2();
//
// The output would be "b1", "a2".
function delegateMethod(delegator, delegatee, methodName) {
var inherited = delegator[methodName];
delegator[methodName] = function() {
var target = delegatee;
var method = delegatee[methodName];
// The method doesn't exist on the delegatee. Instead,
// call the method on the delegator, if it exists.
if (!method) {
target = delegator;
method = inherited;
}
if (method) {
return method.apply(target, arguments);
}
};
}
// Implement a vague facsimilie of jQuery's data method
function elementData(el, name, value) {
if (arguments.length == 2) {
return el["htmlwidget_data_" + name];
} else if (arguments.length == 3) {
el["htmlwidget_data_" + name] = value;
return el;
} else {
throw new Error("Wrong number of arguments for elementData: " +
arguments.length);
}
}
// http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
function escapeRegExp(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}
function hasClass(el, className) {
var re = new RegExp("\\b" + escapeRegExp(className) + "\\b");
return re.test(el.className);
}
// elements - array (or array-like object) of HTML elements
// className - class name to test for
// include - if true, only return elements with given className;
// if false, only return elements *without* given className
function filterByClass(elements, className, include) {
var results = [];
for (var i = 0; i < elements.length; i++) {
if (hasClass(elements[i], className) == include)
results.push(elements[i]);
}
return results;
}
function on(obj, eventName, func) {
if (obj.addEventListener) {
obj.addEventListener(eventName, func, false);
} else if (obj.attachEvent) {
obj.attachEvent(eventName, func);
}
}
function off(obj, eventName, func) {
if (obj.removeEventListener)
obj.removeEventListener(eventName, func, false);
else if (obj.detachEvent) {
obj.detachEvent(eventName, func);
}
}
// Translate array of values to top/right/bottom/left, as usual with
// the "padding" CSS property
// https://developer.mozilla.org/en-US/docs/Web/CSS/padding
function unpackPadding(value) {
if (typeof(value) === "number")
value = [value];
if (value.length === 1) {
return {top: value[0], right: value[0], bottom: value[0], left: value[0]};
}
if (value.length === 2) {
return {top: value[0], right: value[1], bottom: value[0], left: value[1]};
}
if (value.length === 3) {
return {top: value[0], right: value[1], bottom: value[2], left: value[1]};
}
if (value.length === 4) {
return {top: value[0], right: value[1], bottom: value[2], left: value[3]};
}
}
// Convert an unpacked padding object to a CSS value
function paddingToCss(paddingObj) {
return paddingObj.top + "px " + paddingObj.right + "px " + paddingObj.bottom + "px " + paddingObj.left + "px";
}
// Makes a number suitable for CSS
function px(x) {
if (typeof(x) === "number")
return x + "px";
else
return x;
}
// Retrieves runtime widget sizing information for an element.
// The return value is either null, or an object with fill, padding,
// defaultWidth, defaultHeight fields.
function sizingPolicy(el) {
var sizingEl = document.querySelector("script[data-for='" + el.id + "'][type='application/htmlwidget-sizing']");
if (!sizingEl)
return null;
var sp = JSON.parse(sizingEl.textContent || sizingEl.text || "{}");
if (viewerMode) {
return sp.viewer;
} else {
return sp.browser;
}
}
// @param tasks Array of strings (or falsy value, in which case no-op).
// Each element must be a valid JavaScript expression that yields a
// function. Or, can be an array of objects with "code" and "data"
// properties; in this case, the "code" property should be a string
// of JS that's an expr that yields a function, and "data" should be
// an object that will be added as an additional argument when that
// function is called.
// @param target The object that will be "this" for each function
// execution.
// @param args Array of arguments to be passed to the functions. (The
// same arguments will be passed to all functions.)
function evalAndRun(tasks, target, args) {
if (tasks) {
forEach(tasks, function(task) {
var theseArgs = args;
if (typeof(task) === "object") {
theseArgs = theseArgs.concat([task.data]);
task = task.code;
}
var taskFunc = tryEval(task);
if (typeof(taskFunc) !== "function") {
throw new Error("Task must be a function! Source:\n" + task);
}
taskFunc.apply(target, theseArgs);
});
}
}
// Attempt eval() both with and without enclosing in parentheses.
// Note that enclosing coerces a function declaration into
// an expression that eval() can parse
// (otherwise, a SyntaxError is thrown)
function tryEval(code) {
var result = null;
try {
result = eval("(" + code + ")");
} catch(error) {
if (!(error instanceof SyntaxError)) {
throw error;
}
try {
result = eval(code);
} catch(e) {
if (e instanceof SyntaxError) {
throw error;
} else {
throw e;
}
}
}
return result;
}
function initSizing(el) {
var sizing = sizingPolicy(el);
if (!sizing)
return;
var cel = document.getElementById("htmlwidget_container");
if (!cel)
return;
if (typeof(sizing.padding) !== "undefined") {
document.body.style.margin = "0";
document.body.style.padding = paddingToCss(unpackPadding(sizing.padding));
}
if (sizing.fill) {
document.body.style.overflow = "hidden";
document.body.style.width = "100%";
document.body.style.height = "100%";
document.documentElement.style.width = "100%";
document.documentElement.style.height = "100%";
cel.style.position = "absolute";
var pad = unpackPadding(sizing.padding);
cel.style.top = pad.top + "px";
cel.style.right = pad.right + "px";
cel.style.bottom = pad.bottom + "px";
cel.style.left = pad.left + "px";
el.style.width = "100%";
el.style.height = "100%";
return {
getWidth: function() { return cel.getBoundingClientRect().width; },
getHeight: function() { return cel.getBoundingClientRect().height; }
};
} else {
el.style.width = px(sizing.width);
el.style.height = px(sizing.height);
return {
getWidth: function() { return cel.getBoundingClientRect().width; },
getHeight: function() { return cel.getBoundingClientRect().height; }
};
}
}
// Default implementations for methods
var defaults = {
find: function(scope) {
return querySelectorAll(scope, "." + this.name);
},
renderError: function(el, err) {
var $el = $(el);
this.clearError(el);
// Add all these error classes, as Shiny does
var errClass = "shiny-output-error";
if (err.type !== null) {
// use the classes of the error condition as CSS class names
errClass = errClass + " " + $.map(asArray(err.type), function(type) {
return errClass + "-" + type;
}).join(" ");
}
errClass = errClass + " htmlwidgets-error";
// Is el inline or block? If inline or inline-block, just display:none it
// and add an inline error.
var display = $el.css("display");
$el.data("restore-display-mode", display);
if (display === "inline" || display === "inline-block") {
$el.hide();
if (err.message !== "") {
var errorSpan = $("").addClass(errClass);
errorSpan.text(err.message);
$el.after(errorSpan);
}
} else if (display === "block") {
// If block, add an error just after the el, set visibility:none on the
// el, and position the error to be on top of the el.
// Mark it with a unique ID and CSS class so we can remove it later.
$el.css("visibility", "hidden");
if (err.message !== "") {
var errorDiv = $("
").addClass(errClass).css("position", "absolute")
.css("top", el.offsetTop)
.css("left", el.offsetLeft)
// setting width can push out the page size, forcing otherwise
// unnecessary scrollbars to appear and making it impossible for
// the element to shrink; so use max-width instead
.css("maxWidth", el.offsetWidth)
.css("height", el.offsetHeight);
errorDiv.text(err.message);
$el.after(errorDiv);
// Really dumb way to keep the size/position of the error in sync with
// the parent element as the window is resized or whatever.
var intId = setInterval(function() {
if (!errorDiv[0].parentElement) {
clearInterval(intId);
return;
}
errorDiv
.css("top", el.offsetTop)
.css("left", el.offsetLeft)
.css("maxWidth", el.offsetWidth)
.css("height", el.offsetHeight);
}, 500);
}
}
},
clearError: function(el) {
var $el = $(el);
var display = $el.data("restore-display-mode");
$el.data("restore-display-mode", null);
if (display === "inline" || display === "inline-block") {
if (display)
$el.css("display", display);
$(el.nextSibling).filter(".htmlwidgets-error").remove();
} else if (display === "block"){
$el.css("visibility", "inherit");
$(el.nextSibling).filter(".htmlwidgets-error").remove();
}
},
sizing: {}
};
// Called by widget bindings to register a new type of widget. The definition
// object can contain the following properties:
// - name (required) - A string indicating the binding name, which will be
// used by default as the CSS classname to look for.
// - initialize (optional) - A function(el) that will be called once per
// widget element; if a value is returned, it will be passed as the third
// value to renderValue.
// - renderValue (required) - A function(el, data, initValue) that will be
// called with data. Static contexts will cause this to be called once per
// element; Shiny apps will cause this to be called multiple times per
// element, as the data changes.
window.HTMLWidgets.widget = function(definition) {
if (!definition.name) {
throw new Error("Widget must have a name");
}
if (!definition.type) {
throw new Error("Widget must have a type");
}
// Currently we only support output widgets
if (definition.type !== "output") {
throw new Error("Unrecognized widget type '" + definition.type + "'");
}
// TODO: Verify that .name is a valid CSS classname
// Support new-style instance-bound definitions. Old-style class-bound
// definitions have one widget "object" per widget per type/class of
// widget; the renderValue and resize methods on such widget objects
// take el and instance arguments, because the widget object can't
// store them. New-style instance-bound definitions have one widget
// object per widget instance; the definition that's passed in doesn't
// provide renderValue or resize methods at all, just the single method
// factory(el, width, height)
// which returns an object that has renderValue(x) and resize(w, h).
// This enables a far more natural programming style for the widget
// author, who can store per-instance state using either OO-style
// instance fields or functional-style closure variables (I guess this
// is in contrast to what can only be called C-style pseudo-OO which is
// what we required before).
if (definition.factory) {
definition = createLegacyDefinitionAdapter(definition);
}
if (!definition.renderValue) {
throw new Error("Widget must have a renderValue function");
}
// For static rendering (non-Shiny), use a simple widget registration
// scheme. We also use this scheme for Shiny apps/documents that also
// contain static widgets.
window.HTMLWidgets.widgets = window.HTMLWidgets.widgets || [];
// Merge defaults into the definition; don't mutate the original definition.
var staticBinding = extend({}, defaults, definition);
overrideMethod(staticBinding, "find", function(superfunc) {
return function(scope) {
var results = superfunc(scope);
// Filter out Shiny outputs, we only want the static kind
return filterByClass(results, "html-widget-output", false);
};
});
window.HTMLWidgets.widgets.push(staticBinding);
if (shinyMode) {
// Shiny is running. Register the definition with an output binding.
// The definition itself will not be the output binding, instead
// we will make an output binding object that delegates to the
// definition. This is because we foolishly used the same method
// name (renderValue) for htmlwidgets definition and Shiny bindings
// but they actually have quite different semantics (the Shiny
// bindings receive data that includes lots of metadata that it
// strips off before calling htmlwidgets renderValue). We can't
// just ignore the difference because in some widgets it's helpful
// to call this.renderValue() from inside of resize(), and if
// we're not delegating, then that call will go to the Shiny
// version instead of the htmlwidgets version.
// Merge defaults with definition, without mutating either.
var bindingDef = extend({}, defaults, definition);
// This object will be our actual Shiny binding.
var shinyBinding = new Shiny.OutputBinding();
// With a few exceptions, we'll want to simply use the bindingDef's
// version of methods if they are available, otherwise fall back to
// Shiny's defaults. NOTE: If Shiny's output bindings gain additional
// methods in the future, and we want them to be overrideable by
// HTMLWidget binding definitions, then we'll need to add them to this
// list.
delegateMethod(shinyBinding, bindingDef, "getId");
delegateMethod(shinyBinding, bindingDef, "onValueChange");
delegateMethod(shinyBinding, bindingDef, "onValueError");
delegateMethod(shinyBinding, bindingDef, "renderError");
delegateMethod(shinyBinding, bindingDef, "clearError");
delegateMethod(shinyBinding, bindingDef, "showProgress");
// The find, renderValue, and resize are handled differently, because we
// want to actually decorate the behavior of the bindingDef methods.
shinyBinding.find = function(scope) {
var results = bindingDef.find(scope);
// Only return elements that are Shiny outputs, not static ones
var dynamicResults = results.filter(".html-widget-output");
// It's possible that whatever caused Shiny to think there might be
// new dynamic outputs, also caused there to be new static outputs.
// Since there might be lots of different htmlwidgets bindings, we
// schedule execution for later--no need to staticRender multiple
// times.
if (results.length !== dynamicResults.length)
scheduleStaticRender();
return dynamicResults;
};
// Wrap renderValue to handle initialization, which unfortunately isn't
// supported natively by Shiny at the time of this writing.
shinyBinding.renderValue = function(el, data) {
Shiny.renderDependencies(data.deps);
// Resolve strings marked as javascript literals to objects
if (!(data.evals instanceof Array)) data.evals = [data.evals];
for (var i = 0; data.evals && i < data.evals.length; i++) {
window.HTMLWidgets.evaluateStringMember(data.x, data.evals[i]);
}
if (!bindingDef.renderOnNullValue) {
if (data.x === null) {
el.style.visibility = "hidden";
return;
} else {
el.style.visibility = "inherit";
}
}
if (!elementData(el, "initialized")) {
initSizing(el);
elementData(el, "initialized", true);
if (bindingDef.initialize) {
var rect = el.getBoundingClientRect();
var result = bindingDef.initialize(el, rect.width, rect.height);
elementData(el, "init_result", result);
}
}
bindingDef.renderValue(el, data.x, elementData(el, "init_result"));
evalAndRun(data.jsHooks.render, elementData(el, "init_result"), [el, data.x]);
};
// Only override resize if bindingDef implements it
if (bindingDef.resize) {
shinyBinding.resize = function(el, width, height) {
// Shiny can call resize before initialize/renderValue have been
// called, which doesn't make sense for widgets.
if (elementData(el, "initialized")) {
bindingDef.resize(el, width, height, elementData(el, "init_result"));
}
};
}
Shiny.outputBindings.register(shinyBinding, bindingDef.name);
}
};
var scheduleStaticRenderTimerId = null;
function scheduleStaticRender() {
if (!scheduleStaticRenderTimerId) {
scheduleStaticRenderTimerId = setTimeout(function() {
scheduleStaticRenderTimerId = null;
window.HTMLWidgets.staticRender();
}, 1);
}
}
// Render static widgets after the document finishes loading
// Statically render all elements that are of this widget's class
window.HTMLWidgets.staticRender = function() {
var bindings = window.HTMLWidgets.widgets || [];
forEach(bindings, function(binding) {
var matches = binding.find(document.documentElement);
forEach(matches, function(el) {
var sizeObj = initSizing(el, binding);
var getSize = function(el) {
if (sizeObj) {
return {w: sizeObj.getWidth(), h: sizeObj.getHeight()}
} else {
var rect = el.getBoundingClientRect();
return {w: rect.width, h: rect.height}
}
};
if (hasClass(el, "html-widget-static-bound"))
return;
el.className = el.className + " html-widget-static-bound";
var initResult;
if (binding.initialize) {
var size = getSize(el);
initResult = binding.initialize(el, size.w, size.h);
elementData(el, "init_result", initResult);
}
if (binding.resize) {
var lastSize = getSize(el);
var resizeHandler = function(e) {
var size = getSize(el);
if (size.w === 0 && size.h === 0)
return;
if (size.w === lastSize.w && size.h === lastSize.h)
return;
lastSize = size;
binding.resize(el, size.w, size.h, initResult);
};
on(window, "resize", resizeHandler);
// This is needed for cases where we're running in a Shiny
// app, but the widget itself is not a Shiny output, but
// rather a simple static widget. One example of this is
// an rmarkdown document that has runtime:shiny and widget
// that isn't in a render function. Shiny only knows to
// call resize handlers for Shiny outputs, not for static
// widgets, so we do it ourselves.
if (window.jQuery) {
window.jQuery(document).on(
"shown.htmlwidgets shown.bs.tab.htmlwidgets shown.bs.collapse.htmlwidgets",
resizeHandler
);
window.jQuery(document).on(
"hidden.htmlwidgets hidden.bs.tab.htmlwidgets hidden.bs.collapse.htmlwidgets",
resizeHandler
);
}
// This is needed for the specific case of ioslides, which
// flips slides between display:none and display:block.
// Ideally we would not have to have ioslide-specific code
// here, but rather have ioslides raise a generic event,
// but the rmarkdown package just went to CRAN so the
// window to getting that fixed may be long.
if (window.addEventListener) {
// It's OK to limit this to window.addEventListener
// browsers because ioslides itself only supports
// such browsers.
on(document, "slideenter", resizeHandler);
on(document, "slideleave", resizeHandler);
}
}
var scriptData = document.querySelector("script[data-for='" + el.id + "'][type='application/json']");
if (scriptData) {
var data = JSON.parse(scriptData.textContent || scriptData.text);
// Resolve strings marked as javascript literals to objects
if (!(data.evals instanceof Array)) data.evals = [data.evals];
for (var k = 0; data.evals && k < data.evals.length; k++) {
window.HTMLWidgets.evaluateStringMember(data.x, data.evals[k]);
}
binding.renderValue(el, data.x, initResult);
evalAndRun(data.jsHooks.render, initResult, [el, data.x]);
}
});
});
invokePostRenderHandlers();
}
function has_jQuery3() {
if (!window.jQuery) {
return false;
}
var $version = window.jQuery.fn.jquery;
var $major_version = parseInt($version.split(".")[0]);
return $major_version >= 3;
}
/*
/ Shiny 1.4 bumped jQuery from 1.x to 3.x which means jQuery's
/ on-ready handler (i.e., $(fn)) is now asyncronous (i.e., it now
/ really means $(setTimeout(fn)).
/ https://jquery.com/upgrade-guide/3.0/#breaking-change-document-ready-handlers-are-now-asynchronous
/
/ Since Shiny uses $() to schedule initShiny, shiny>=1.4 calls initShiny
/ one tick later than it did before, which means staticRender() is
/ called renderValue() earlier than (advanced) widget authors might be expecting.
/ https://github.com/rstudio/shiny/issues/2630
/
/ For a concrete example, leaflet has some methods (e.g., updateBounds)
/ which reference Shiny methods registered in initShiny (e.g., setInputValue).
/ Since leaflet is privy to this life-cycle, it knows to use setTimeout() to
/ delay execution of those methods (until Shiny methods are ready)
/ https://github.com/rstudio/leaflet/blob/18ec981/javascript/src/index.js#L266-L268
/
/ Ideally widget authors wouldn't need to use this setTimeout() hack that
/ leaflet uses to call Shiny methods on a staticRender(). In the long run,
/ the logic initShiny should be broken up so that method registration happens
/ right away, but binding happens later.
*/
function maybeStaticRenderLater() {
if (shinyMode && has_jQuery3()) {
window.jQuery(window.HTMLWidgets.staticRender);
} else {
window.HTMLWidgets.staticRender();
}
}
if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", function() {
document.removeEventListener("DOMContentLoaded", arguments.callee, false);
maybeStaticRenderLater();
}, false);
} else if (document.attachEvent) {
document.attachEvent("onreadystatechange", function() {
if (document.readyState === "complete") {
document.detachEvent("onreadystatechange", arguments.callee);
maybeStaticRenderLater();
}
});
}
window.HTMLWidgets.getAttachmentUrl = function(depname, key) {
// If no key, default to the first item
if (typeof(key) === "undefined")
key = 1;
var link = document.getElementById(depname + "-" + key + "-attachment");
if (!link) {
throw new Error("Attachment " + depname + "/" + key + " not found in document");
}
return link.getAttribute("href");
};
window.HTMLWidgets.dataframeToD3 = function(df) {
var names = [];
var length;
for (var name in df) {
if (df.hasOwnProperty(name))
names.push(name);
if (typeof(df[name]) !== "object" || typeof(df[name].length) === "undefined") {
throw new Error("All fields must be arrays");
} else if (typeof(length) !== "undefined" && length !== df[name].length) {
throw new Error("All fields must be arrays of the same length");
}
length = df[name].length;
}
var results = [];
var item;
for (var row = 0; row < length; row++) {
item = {};
for (var col = 0; col < names.length; col++) {
item[names[col]] = df[names[col]][row];
}
results.push(item);
}
return results;
};
window.HTMLWidgets.transposeArray2D = function(array) {
if (array.length === 0) return array;
var newArray = array[0].map(function(col, i) {
return array.map(function(row) {
return row[i]
})
});
return newArray;
};
// Split value at splitChar, but allow splitChar to be escaped
// using escapeChar. Any other characters escaped by escapeChar
// will be included as usual (including escapeChar itself).
function splitWithEscape(value, splitChar, escapeChar) {
var results = [];
var escapeMode = false;
var currentResult = "";
for (var pos = 0; pos < value.length; pos++) {
if (!escapeMode) {
if (value[pos] === splitChar) {
results.push(currentResult);
currentResult = "";
} else if (value[pos] === escapeChar) {
escapeMode = true;
} else {
currentResult += value[pos];
}
} else {
currentResult += value[pos];
escapeMode = false;
}
}
if (currentResult !== "") {
results.push(currentResult);
}
return results;
}
// Function authored by Yihui/JJ Allaire
window.HTMLWidgets.evaluateStringMember = function(o, member) {
var parts = splitWithEscape(member, '.', '\\');
for (var i = 0, l = parts.length; i < l; i++) {
var part = parts[i];
// part may be a character or 'numeric' member name
if (o !== null && typeof o === "object" && part in o) {
if (i == (l - 1)) { // if we are at the end of the line then evalulate
if (typeof o[part] === "string")
o[part] = tryEval(o[part]);
} else { // otherwise continue to next embedded object
o = o[part];
}
}
}
};
// Retrieve the HTMLWidget instance (i.e. the return value of an
// HTMLWidget binding's initialize() or factory() function)
// associated with an element, or null if none.
window.HTMLWidgets.getInstance = function(el) {
return elementData(el, "init_result");
};
// Finds the first element in the scope that matches the selector,
// and returns the HTMLWidget instance (i.e. the return value of
// an HTMLWidget binding's initialize() or factory() function)
// associated with that element, if any. If no element matches the
// selector, or the first matching element has no HTMLWidget
// instance associated with it, then null is returned.
//
// The scope argument is optional, and defaults to window.document.
window.HTMLWidgets.find = function(scope, selector) {
if (arguments.length == 1) {
selector = scope;
scope = document;
}
var el = scope.querySelector(selector);
if (el === null) {
return null;
} else {
return window.HTMLWidgets.getInstance(el);
}
};
// Finds all elements in the scope that match the selector, and
// returns the HTMLWidget instances (i.e. the return values of
// an HTMLWidget binding's initialize() or factory() function)
// associated with the elements, in an array. If elements that
// match the selector don't have an associated HTMLWidget
// instance, the returned array will contain nulls.
//
// The scope argument is optional, and defaults to window.document.
window.HTMLWidgets.findAll = function(scope, selector) {
if (arguments.length == 1) {
selector = scope;
scope = document;
}
var nodes = scope.querySelectorAll(selector);
var results = [];
for (var i = 0; i < nodes.length; i++) {
results.push(window.HTMLWidgets.getInstance(nodes[i]));
}
return results;
};
var postRenderHandlers = [];
function invokePostRenderHandlers() {
while (postRenderHandlers.length) {
var handler = postRenderHandlers.shift();
if (handler) {
handler();
}
}
}
// Register the given callback function to be invoked after the
// next time static widgets are rendered.
window.HTMLWidgets.addPostRenderHandler = function(callback) {
postRenderHandlers.push(callback);
};
// Takes a new-style instance-bound definition, and returns an
// old-style class-bound definition. This saves us from having
// to rewrite all the logic in this file to accomodate both
// types of definitions.
function createLegacyDefinitionAdapter(defn) {
var result = {
name: defn.name,
type: defn.type,
initialize: function(el, width, height) {
return defn.factory(el, width, height);
},
renderValue: function(el, x, instance) {
return instance.renderValue(x);
},
resize: function(el, width, height, instance) {
return instance.resize(width, height);
}
};
if (defn.find)
result.find = defn.find;
if (defn.renderError)
result.renderError = defn.renderError;
if (defn.clearError)
result.clearError = defn.clearError;
return result;
}
})();
* vis-network
* https://visjs.github.io/vis-network/
*
* A dynamic, browser-based visualization library.
*
* @version 0.0.0-no-version
* @date 2021-09-28T12:03:00.413Z
*
* @copyright (c) 2011-2017 Almende B.V, http://almende.com
* @copyright (c) 2017-2019 visjs contributors, https://github.com/visjs
*
* @license
* vis.js is dual licensed under both
*
* 1. The Apache 2.0 License
* http://www.apache.org/licenses/LICENSE-2.0
*
* and
*
* 2. The MIT License
* http://opensource.org/licenses/MIT
*
* vis.js may be distributed under either license.
*/
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).vis=t.vis||{})}(this,(function(t){"use strict";var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function i(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}function n(t,e){return t(e={exports:{}},e.exports),e.exports}var o,r,s=function(t){return t&&t.Math==Math&&t},a=s("object"==typeof globalThis&&globalThis)||s("object"==typeof window&&window)||s("object"==typeof self&&self)||s("object"==typeof e&&e)||function(){return this}()||Function("return this")(),h=function(t){try{return!!t()}catch(t){return!0}},l=!h((function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]})),d={}.propertyIsEnumerable,c=Object.getOwnPropertyDescriptor,u={f:c&&!d.call({1:2},1)?function(t){var e=c(this,t);return!!e&&e.enumerable}:d},f=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}},p={}.toString,v=function(t){return p.call(t).slice(8,-1)},g="".split,y=h((function(){return!Object("z").propertyIsEnumerable(0)}))?function(t){return"String"==v(t)?g.call(t,""):Object(t)}:Object,m=function(t){if(null==t)throw TypeError("Can't call method on "+t);return t},b=function(t){return y(m(t))},w=function(t){return"object"==typeof t?null!==t:"function"==typeof t},k={},_=function(t){return"function"==typeof t?t:void 0},x=function(t,e){return arguments.length<2?_(k[t])||_(a[t]):k[t]&&k[t][e]||a[t]&&a[t][e]},E=x("navigator","userAgent")||"",O=a.process,C=a.Deno,S=O&&O.versions||C&&C.version,T=S&&S.v8;T?r=(o=T.split("."))[0]<4?1:o[0]+o[1]:E&&(!(o=E.match(/Edge\/(\d+)/))||o[1]>=74)&&(o=E.match(/Chrome\/(\d+)/))&&(r=o[1]);var M=r&&+r,P=!!Object.getOwnPropertySymbols&&!h((function(){var t=Symbol();return!String(t)||!(Object(t)instanceof Symbol)||!Symbol.sham&&M&&M<41})),D=P&&!Symbol.sham&&"symbol"==typeof Symbol.iterator,I=D?function(t){return"symbol"==typeof t}:function(t){var e=x("Symbol");return"function"==typeof e&&Object(t)instanceof e},B="__core-js_shared__",z=a[B]||function(t,e){try{Object.defineProperty(a,t,{value:e,configurable:!0,writable:!0})}catch(i){a[t]=e}return e}(B,{}),N=n((function(t){(t.exports=function(t,e){return z[t]||(z[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.16.1",mode:"pure",copyright:"© 2021 Denis Pushkarev (zloirock.ru)"})})),A=function(t){return Object(m(t))},F={}.hasOwnProperty,j=Object.hasOwn||function(t,e){return F.call(A(t),e)},R=0,L=Math.random(),H=function(t){return"Symbol("+String(void 0===t?"":t)+")_"+(++R+L).toString(36)},W=N("wks"),q=a.Symbol,V=D?q:q&&q.withoutSetter||H,U=function(t){return j(W,t)&&(P||"string"==typeof W[t])||(P&&j(q,t)?W[t]=q[t]:W[t]=V("Symbol."+t)),W[t]},Y=U("toPrimitive"),X=function(t,e){if(!w(t)||I(t))return t;var i,n=t[Y];if(void 0!==n){if(void 0===e&&(e="default"),i=n.call(t,e),!w(i)||I(i))return i;throw TypeError("Can't convert object to primitive value")}return void 0===e&&(e="number"),function(t,e){var i,n;if("string"===e&&"function"==typeof(i=t.toString)&&!w(n=i.call(t)))return n;if("function"==typeof(i=t.valueOf)&&!w(n=i.call(t)))return n;if("string"!==e&&"function"==typeof(i=t.toString)&&!w(n=i.call(t)))return n;throw TypeError("Can't convert object to primitive value")}(t,e)},G=function(t){var e=X(t,"string");return I(e)?e:String(e)},K=a.document,$=w(K)&&w(K.createElement),Z=function(t){return $?K.createElement(t):{}},Q=!l&&!h((function(){return 7!=Object.defineProperty(Z("div"),"a",{get:function(){return 7}}).a})),J=Object.getOwnPropertyDescriptor,tt={f:l?J:function(t,e){if(t=b(t),e=G(e),Q)try{return J(t,e)}catch(t){}if(j(t,e))return f(!u.f.call(t,e),t[e])}},et=/#|\.prototype\./,it=function(t,e){var i=ot[nt(t)];return i==st||i!=rt&&("function"==typeof e?h(e):!!e)},nt=it.normalize=function(t){return String(t).replace(et,".").toLowerCase()},ot=it.data={},rt=it.NATIVE="N",st=it.POLYFILL="P",at=it,ht=function(t){if("function"!=typeof t)throw TypeError(String(t)+" is not a function");return t},lt=function(t,e,i){if(ht(t),void 0===e)return t;switch(i){case 0:return function(){return t.call(e)};case 1:return function(i){return t.call(e,i)};case 2:return function(i,n){return t.call(e,i,n)};case 3:return function(i,n,o){return t.call(e,i,n,o)}}return function(){return t.apply(e,arguments)}},dt=function(t){if(!w(t))throw TypeError(String(t)+" is not an object");return t},ct=Object.defineProperty,ut={f:l?ct:function(t,e,i){if(dt(t),e=G(e),dt(i),Q)try{return ct(t,e,i)}catch(t){}if("get"in i||"set"in i)throw TypeError("Accessors not supported");return"value"in i&&(t[e]=i.value),t}},ft=l?function(t,e,i){return ut.f(t,e,f(1,i))}:function(t,e,i){return t[e]=i,t},pt=tt.f,vt=function(t){var e=function(e,i,n){if(this instanceof t){switch(arguments.length){case 0:return new t;case 1:return new t(e);case 2:return new t(e,i)}return new t(e,i,n)}return t.apply(this,arguments)};return e.prototype=t.prototype,e},gt=function(t,e){var i,n,o,r,s,h,l,d,c=t.target,u=t.global,f=t.stat,p=t.proto,v=u?a:f?a[c]:(a[c]||{}).prototype,g=u?k:k[c]||(k[c]={}),y=g.prototype;for(o in e)i=!at(u?o:c+(f?".":"#")+o,t.forced)&&v&&j(v,o),s=g[o],i&&(h=t.noTargetGet?(d=pt(v,o))&&d.value:v[o]),r=i&&h?h:e[o],i&&typeof s==typeof r||(l=t.bind&&i?lt(r,a):t.wrap&&i?vt(r):p&&"function"==typeof r?lt(Function.call,r):r,(t.sham||r&&r.sham||s&&s.sham)&&ft(l,"sham",!0),g[o]=l,p&&(j(k,n=c+"Prototype")||ft(k,n,{}),k[n][o]=r,t.real&&y&&!y[o]&&ft(y,o,r)))},yt=Math.ceil,mt=Math.floor,bt=function(t){return isNaN(t=+t)?0:(t>0?mt:yt)(t)},wt=Math.min,kt=function(t){return t>0?wt(bt(t),9007199254740991):0},_t=Math.max,xt=Math.min,Et=function(t,e){var i=bt(t);return i<0?_t(i+e,0):xt(i,e)},Ot=function(t){return function(e,i,n){var o,r=b(e),s=kt(r.length),a=Et(n,s);if(t&&i!=i){for(;s>a;)if((o=r[a++])!=o)return!0}else for(;s>a;a++)if((t||a in r)&&r[a]===i)return t||a||0;return!t&&-1}},Ct={includes:Ot(!0),indexOf:Ot(!1)},St={},Tt=Ct.indexOf,Mt=function(t,e){var i,n=b(t),o=0,r=[];for(i in n)!j(St,i)&&j(n,i)&&r.push(i);for(;e.length>o;)j(n,i=e[o++])&&(~Tt(r,i)||r.push(i));return r},Pt=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],Dt=Object.keys||function(t){return Mt(t,Pt)},It={f:Object.getOwnPropertySymbols},Bt=Object.assign,zt=Object.defineProperty,Nt=!Bt||h((function(){if(l&&1!==Bt({b:1},Bt(zt({},"a",{enumerable:!0,get:function(){zt(this,"b",{value:3,enumerable:!1})}}),{b:2})).b)return!0;var t={},e={},i=Symbol(),n="abcdefghijklmnopqrst";return t[i]=7,n.split("").forEach((function(t){e[t]=t})),7!=Bt({},t)[i]||Dt(Bt({},e)).join("")!=n}))?function(t,e){for(var i=A(t),n=arguments.length,o=1,r=It.f,s=u.f;n>o;)for(var a,h=y(arguments[o++]),d=r?Dt(h).concat(r(h)):Dt(h),c=d.length,f=0;c>f;)a=d[f++],l&&!s.call(h,a)||(i[a]=h[a]);return i}:Bt;gt({target:"Object",stat:!0,forced:Object.assign!==Nt},{assign:Nt});var At=k.Object.assign,Ft=[].slice,jt={},Rt=function(t,e,i){if(!(e in jt)){for(var n=[],o=0;o=.1;)(p=+r[c++%s])>d&&(p=d),f=Math.sqrt(p*p/(1+l*l)),e+=f=a<0?-f:f,i+=l*f,!0===u?t.lineTo(e,i):t.moveTo(e,i),d-=p,u=!u}var $t={circle:Ut,dashedLine:Kt,database:Gt,diamond:function(t,e,i,n){t.beginPath(),t.lineTo(e,i+n),t.lineTo(e+n,i),t.lineTo(e,i-n),t.lineTo(e-n,i),t.closePath()},ellipse:Xt,ellipse_vis:Xt,hexagon:function(t,e,i,n){t.beginPath();var o=2*Math.PI/6;t.moveTo(e+n,i);for(var r=1;r<6;r++)t.lineTo(e+n*Math.cos(o*r),i+n*Math.sin(o*r));t.closePath()},roundRect:Yt,square:function(t,e,i,n){t.beginPath(),t.rect(e-n,i-n,2*n,2*n),t.closePath()},star:function(t,e,i,n){t.beginPath(),i+=.1*(n*=.82);for(var o=0;o<10;o++){var r=o%2==0?1.3*n:.5*n;t.lineTo(e+r*Math.sin(2*o*Math.PI/10),i-r*Math.cos(2*o*Math.PI/10))}t.closePath()},triangle:function(t,e,i,n){t.beginPath(),i+=.275*(n*=1.15);var o=2*n,r=o/2,s=Math.sqrt(3)/6*o,a=Math.sqrt(o*o-r*r);t.moveTo(e,i-(a-s)),t.lineTo(e+r,i+s),t.lineTo(e-r,i+s),t.lineTo(e,i-(a-s)),t.closePath()},triangleDown:function(t,e,i,n){t.beginPath(),i-=.275*(n*=1.15);var o=2*n,r=o/2,s=Math.sqrt(3)/6*o,a=Math.sqrt(o*o-r*r);t.moveTo(e,i+(a-s)),t.lineTo(e+r,i-s),t.lineTo(e-r,i-s),t.lineTo(e,i+(a-s)),t.closePath()}};var Zt=n((function(t){function e(t){if(t)return function(t){for(var i in e.prototype)t[i]=e.prototype[i];return t}(t)}t.exports=e,e.prototype.on=e.prototype.addEventListener=function(t,e){return this._callbacks=this._callbacks||{},(this._callbacks["$"+t]=this._callbacks["$"+t]||[]).push(e),this},e.prototype.once=function(t,e){function i(){this.off(t,i),e.apply(this,arguments)}return i.fn=e,this.on(t,i),this},e.prototype.off=e.prototype.removeListener=e.prototype.removeAllListeners=e.prototype.removeEventListener=function(t,e){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var i,n=this._callbacks["$"+t];if(!n)return this;if(1==arguments.length)return delete this._callbacks["$"+t],this;for(var o=0;o=a?t?"":void 0:(n=r.charCodeAt(s))<55296||n>56319||s+1===a||(o=r.charCodeAt(s+1))<56320||o>57343?t?r.charAt(s):n:t?r.slice(s,s+2):o-56320+(n-55296<<10)+65536}},te={codeAt:Jt(!1),charAt:Jt(!0)},ee=Function.toString;"function"!=typeof z.inspectSource&&(z.inspectSource=function(t){return ee.call(t)});var ie,ne,oe,re=z.inspectSource,se=a.WeakMap,ae="function"==typeof se&&/native code/.test(re(se)),he=N("keys"),le=function(t){return he[t]||(he[t]=H(t))},de="Object already initialized",ce=a.WeakMap;if(ae||z.state){var ue=z.state||(z.state=new ce),fe=ue.get,pe=ue.has,ve=ue.set;ie=function(t,e){if(pe.call(ue,t))throw new TypeError(de);return e.facade=t,ve.call(ue,t,e),e},ne=function(t){return fe.call(ue,t)||{}},oe=function(t){return pe.call(ue,t)}}else{var ge=le("state");St[ge]=!0,ie=function(t,e){if(j(t,ge))throw new TypeError(de);return e.facade=t,ft(t,ge,e),e},ne=function(t){return j(t,ge)?t[ge]:{}},oe=function(t){return j(t,ge)}}var ye,me,be,we={set:ie,get:ne,has:oe,enforce:function(t){return oe(t)?ne(t):ie(t,{})},getterFor:function(t){return function(e){var i;if(!w(e)||(i=ne(e)).type!==t)throw TypeError("Incompatible receiver, "+t+" required");return i}}},ke=!h((function(){function t(){}return t.prototype.constructor=null,Object.getPrototypeOf(new t)!==t.prototype})),_e=le("IE_PROTO"),xe=Object.prototype,Ee=ke?Object.getPrototypeOf:function(t){return t=A(t),j(t,_e)?t[_e]:"function"==typeof t.constructor&&t instanceof t.constructor?t.constructor.prototype:t instanceof Object?xe:null},Oe=U("iterator"),Ce=!1;[].keys&&("next"in(be=[].keys())?(me=Ee(Ee(be)))!==Object.prototype&&(ye=me):Ce=!0);var Se=null==ye||h((function(){var t={};return ye[Oe].call(t)!==t}));Se&&(ye={}),Se&&!j(ye,Oe)&&ft(ye,Oe,(function(){return this}));var Te,Me={IteratorPrototype:ye,BUGGY_SAFARI_ITERATORS:Ce},Pe=l?Object.defineProperties:function(t,e){dt(t);for(var i,n=Dt(e),o=n.length,r=0;o>r;)ut.f(t,i=n[r++],e[i]);return t},De=x("document","documentElement"),Ie=le("IE_PROTO"),Be=function(){},ze=function(t){return"
//# sourceMappingURL=vis-network.min.js.map
// Référence : https://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.from
if (!Array.from) {
Array.from = (function () {
var toStr = Object.prototype.toString;
var isCallable = function (fn) {
return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
};
var toInteger = function (value) {
var number = Number(value);
if (isNaN(number)) { return 0; }
if (number === 0 || !isFinite(number)) { return number; }
return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
};
var maxSafeInteger = Math.pow(2, 53) - 1;
var toLength = function (value) {
var len = toInteger(value);
return Math.min(Math.max(len, 0), maxSafeInteger);
};
// La propriété length de la méthode vaut 1.
return function from(arrayLike/*, mapFn, thisArg */) {
// 1. Soit C, la valeur this
var C = this;
// 2. Soit items le ToObject(arrayLike).
var items = Object(arrayLike);
// 3. ReturnIfAbrupt(items).
if (arrayLike == null) {
throw new TypeError("Array.from doit utiliser un objet semblable à un tableau - null ou undefined ne peuvent pas être utilisés");
}
// 4. Si mapfn est undefined, le mapping sera false.
var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
var T;
if (typeof mapFn !== 'undefined') {
// 5. sinon
// 5. a. si IsCallable(mapfn) est false, on lève une TypeError.
if (!isCallable(mapFn)) {
throw new TypeError('Array.from: lorsqu il est utilisé le deuxième argument doit être une fonction');
}
// 5. b. si thisArg a été fourni, T sera thisArg ; sinon T sera undefined.
if (arguments.length > 2) {
T = arguments[2];
}
}
// 10. Soit lenValue pour Get(items, "length").
// 11. Soit len pour ToLength(lenValue).
var len = toLength(items.length);
// 13. Si IsConstructor(C) vaut true, alors
// 13. a. Soit A le résultat de l'appel à la méthode interne [[Construct]] avec une liste en argument qui contient l'élément len.
// 14. a. Sinon, soit A le résultat de ArrayCreate(len).
var A = isCallable(C) ? Object(new C(len)) : new Array(len);
// 16. Soit k égal à 0.
var k = 0; // 17. On répète tant que k < len…
var kValue;
while (k < len) {
kValue = items[k];
if (mapFn) {
A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
} else {
A[k] = kValue;
}
k += 1;
}
// 18. Soit putStatus égal à Put(A, "length", len, true).
A.length = len; // 20. On renvoie A.
return A;
};
}());
};
// https://tc39.github.io/ecma262/#sec-array.prototype.includes
if (!Array.prototype.includes) {
Object.defineProperty(Array.prototype, 'includes', {
value: function(searchElement, fromIndex) {
if (this == null) {
throw new TypeError('"this" est nul ou non défini');
}
// 1. Soit o égal à ? Object(cette valeur).
var o = Object(this);
// 2. Soit len égal à ? Length(? Get(o, "length")).
var len = o.length >>> 0;
// 3. Si len = 0, renvoyer "false".
if (len === 0) {
return false;
}
// 4. Soit n = ? ToInteger(fromIndex).
// Pour la cohérence du code, on gardera le nom anglais "fromIndex" pour la variable auparavant appelée "indiceDépart"
// (Si fromIndex n'est pas défini, cette étape produit la valeur 0.)
var n = fromIndex | 0;
// 5. Si n ≥ 0,
// a. Alors k = n.
// 6. Sinon, si n < 0,
// a. Alors k = len + n.
// b. Si k < 0, alors k = 0.
var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
function sameValueZero(x, y) {
return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y));
}
// 7. Répéter tant que k < len
while (k < len) {
// a. Soit elementK le résultat de ? Get(O, ! ToString(k)).
// b. Si SameValueZero(searchElement, elementK) est vrai, renvoyer "true".
if (sameValueZero(o[k], searchElement)) {
return true;
}
// c. Augmenter la valeur de k de 1.
k++;
}
// 8. Renvoyer "false"
return false;
}
});
}
// Add shim for Function.prototype.bind() from:
// https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/bind#Compatibility
// for fix some RStudio viewer bug (Desktop / windows)
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
// closest thing possible to the ECMAScript 5 internal IsCallable function
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
return fToBind.apply(this instanceof fNOP && oThis
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
return fBound;
};
}
//--------------------------------------------
// functions to reset edges after hard to read
//--------------------------------------------
// for edges
function edgeAsHardToRead(edge, hideColor1, hideColor2, network, type){
//console.info("edgeAsHardToRead")
if(type === "edge"){
//console.info("edge")
//console.info(edge.id)
// saving color information (if we have)
if (edge.hiddenColor === undefined && edge.color !== hideColor1 && edge.color !== hideColor2) {
edge.hiddenColor = edge.color;
}
// set "hard to read" color
edge.color = hideColor1;
// reset and save label
if (edge.hiddenLabel === undefined) {
edge.hiddenLabel = edge.label;
edge.label = undefined;
}
edge.isHardToRead = true;
} else {
//console.info("cluster")
//console.info(edge.id)
//console.info(edge)
// saving color information (if we have)
if (edge.hiddenColor === undefined && edge.color !== hideColor1 && edge.color !== hideColor2) {
//network.clustering.updateEdge(edge.id, {hiddenColor : edge.color});
edge.hiddenColor = edge.color;
}
// set "hard to read" color
edge.color = hideColor1;
//network.clustering.updateEdge(edge.id, {color : hideColor1});
//edge.color = hideColor1;
// reset and save label
if (edge.hiddenLabel === undefined) {
edge.hiddenLabel = edge.label;
edge.label = undefined;
}
edge.isHardToRead = true;
}
}
function resetOneEdge(edge, hideColor1, hideColor2, type){
/*console.info("resetOneEdge")
console.info(type)
console.info(edge.id)
console.info(edge)
console.info("edge.hiddenColor")
console.info(edge.hiddenColor)*/
var treat_egde = false;
if(type === "cluster"){
if(edge.isHardToRead !== undefined){ // we have to reset this node
if(edge.isHardToRead){
treat_egde = true;
} else if(edge.isHardToRead === false && (edge.color.color === hideColor1 || edge.color.color === hideColor2)){
treat_egde = true;
}
} else if(edge.color.color === hideColor1 || edge.color.color === hideColor2){
treat_egde = true;
}
if(treat_egde){
// get back color
if (edge.hiddenColor !== undefined) {
edge.color = edge.hiddenColor;
edge.hiddenColor = undefined;
}else{
delete edge.color;
}
// finally, get back label
if (edge.hiddenLabel !== undefined) {
edge.label = edge.hiddenLabel;
edge.hiddenLabel = undefined;
}
edge.isHardToRead = false;
}
} else {
// get back color
if (edge.hiddenColor !== undefined) {
edge.color = edge.hiddenColor;
edge.hiddenColor = undefined;
}else{
edge.color = null;
}
// finally, get back label
if (edge.hiddenLabel !== undefined) {
edge.label = edge.hiddenLabel;
edge.hiddenLabel = undefined;
}
edge.isHardToRead = false;
}
}
function resetAllEdges(edges, hideColor1, hideColor2, network){
var edgesToReset = edges.get({
fields: ['id', 'color', 'hiddenColor', 'label', 'hiddenLabel'],
filter: function (item) {
return item.isHardToRead === true;
},
returnType :'Array'
});
var is_cluster_edges = false;
var edges_in_clusters;
if(network !== undefined){
edges_in_clusters = network.body.modules.clustering.clusteredEdges;
if(Object.keys(edges_in_clusters).length > 0){
is_cluster_edges = true;
edges_in_clusters = Object.keys(edges_in_clusters);
} else {
edges_in_clusters = [];
}
}
var treat_edges_in_clusters = [];
// all edges get their own color and their label back
for (var i = 0; i < edgesToReset.length; i++) {
resetOneEdge(edgesToReset[i], hideColor1, hideColor2,type = "edge");
if(is_cluster_edges){
if(indexOf.call(edges_in_clusters, edgesToReset[i].id, true) > -1){
var tmp_cluster_id = network.clustering.getClusteredEdges(edgesToReset[i].id);
if(tmp_cluster_id.length > 1){
tmp_cluster_id = tmp_cluster_id[0];
treat_edges_in_clusters.push(tmp_cluster_id);
resetOneEdge(network.body.edges[tmp_cluster_id].options, hideColor1, hideColor2, type = "cluster");
}
}
}
}
// some misunderstood bug on some cluster edges... so have a (bad) fix...
var edges_in_clusters_ctrl = edges_in_clusters.filter(function(word,index){
if(word.match(/^clusterEdge/i)){
if(indexOf.call(treat_edges_in_clusters, word, true) === -1){
return true;
} else {
return false;
}
}else{
return false;
}
});
if(is_cluster_edges){
if(edges_in_clusters_ctrl.length > 0){
for (var j = 0; j < edges_in_clusters_ctrl.length; j++) {
if(network.body.edges[edges_in_clusters_ctrl[j]] !== undefined){
resetOneEdge(network.body.edges[edges_in_clusters_ctrl[j]].options, hideColor1, hideColor2, type = "cluster");
}
}
}
}
edges.update(edgesToReset);
}
//--------------------------------------------
// functions to reset nodes after hard to read
//--------------------------------------------
// for classic node
function simpleResetNode(node, type){
if(type === "node"){
// get back color
if (node.hiddenColor !== undefined) {
node.color = node.hiddenColor;
node.hiddenColor = undefined;
}else{
if(node.group !== undefined){
node.color = undefined;
} else {
node.color = null;
}
}
} else {
if (Object.keys(node.options.hiddenColor).length > 2){
node.setOptions({color : node.options.hiddenColor, hiddenColor : undefined});
}else{
if(node.options.group !== undefined){
node.setOptions({color : undefined});
} else {
node.setOptions({color : null});
}
}
}
}
// for icon node
function simpleIconResetNode(node, type){
if(type === "node"){
// icon color
node.icon.color = node.hiddenColor;
node.hiddenColor = undefined;
// get back color
if (node.hiddenColorForLabel !== undefined) {
node.color = node.hiddenColorForLabel;
node.hiddenColorForLabel = undefined;
}else{
if(node.group !== undefined){
node.color = undefined;
} else {
node.color = null;
}
}
} else {
node.setOptions({icon : { color : node.options.hiddenColor}, hiddenColor : undefined});
if (node.options.hiddenColorForLabel !== undefined) {
node.setOptions({color : node.options.hiddenColorForLabel, hiddenColorForLabel : undefined});
}else{
if(node.options.group !== undefined){
node.setOptions({color : undefined});
} else {
node.setOptions({color : null});
}
}
}
}
// for image node
function simpleImageResetNode(node, imageType, type){
if(type === "node"){
// get back color
if (node.hiddenColor !== undefined) {
node.color = node.hiddenColor;
node.hiddenColor = undefined;
}else{
if(node.group !== undefined){
node.color = undefined;
} else {
node.color = null;
}
}
// and set shape as image/circularImage
node.shape = imageType;
} else {
if (Object.keys(node.options.hiddenColor).length > 2) {
node.setOptions({color : node.options.hiddenColor, hiddenColor : undefined});
}else{
if(node.options.group !== undefined){
node.setOptions({color : undefined});
} else {
node.setOptions({color : null});
}
}
node.setOptions({shape : imageType});
}
}
// Global function to reset one cluster
function resetOneCluster(node, options, network){
if(node !== undefined){
if(node.options.isHardToRead !== undefined){ // we have to reset this node
if(node.options.isHardToRead){
var final_shape;
var shape_group = false;
var is_group = false;
// have a group information & a shape defined in group ?
if(node.options.group !== undefined && options.groups !== undefined){
if(options.groups[node.options.group] !== undefined){
is_group = true;
if(options.groups[node.options.group].shape !== undefined){
shape_group = true;
}
}
}
// have a global shape in nodes options ?
var shape_options = false;
if(options.nodes !== undefined){
if(options.nodes.shape !== undefined){
shape_options = true;
}
}
// set final shape (individual > group > global)
if(node.options.hiddenImage !== undefined){
final_shape = node.options.hiddenImage;
} else if(node.options.shape !== undefined){
final_shape = node.options.shape;
} else if(shape_group){
final_shape = options.groups[node.options.group].shape;
} else if(shape_options){
final_shape = options.nodes.shape;
}
node.setOptions({bodyHiddenColor : network.body.nodes[node.id].options.color});
// and call good reset function
if(final_shape === "icon"){
simpleIconResetNode(node, "cluster");
} else if(final_shape === "image"){
simpleImageResetNode(node, "image", "cluster");
} else if(final_shape === "circularImage"){
simpleImageResetNode(node, "circularImage", "cluster");
} else {
simpleResetNode(node, "cluster");
}
// finally, get back label
if (node.options.hiddenLabel !== undefined) {
node.setOptions({label : node.options.hiddenLabel, hiddenLabel : undefined});
}
node.options.isHardToRead = false;
}
}
}
}
// Global function to reset one node
function resetOneNode(node, options, network){
if(node !== undefined){
if(node.isHardToRead !== undefined){ // we have to reset this node
if(node.isHardToRead){
var final_shape;
var shape_group = false;
var is_group = false;
// have a group information & a shape defined in group ?
if(node.group !== undefined && options.groups !== undefined){
if(options.groups[node.group] !== undefined){
is_group = true;
if(options.groups[node.group].shape !== undefined){
shape_group = true;
}
}
}
// have a global shape in nodes options ?
var shape_options = false;
if(options.nodes !== undefined){
if(options.nodes.shape !== undefined){
shape_options = true;
}
}
// set final shape (individual > group > global)
if(node.hiddenImage !== undefined){
final_shape = node.hiddenImage;
} else if(node.shape !== undefined){
final_shape = node.shape;
} else if(shape_group){
final_shape = options.groups[node.group].shape;
} else if(shape_options){
final_shape = options.nodes.shape;
}
// reset body information
network.body.nodes[node.id].options.color = node.bodyHiddenColor;
// and call good reset function
if(final_shape === "icon"){
simpleIconResetNode(node, "node");
} else if(final_shape === "image"){
simpleImageResetNode(node, "image", "node");
} else if(final_shape === "circularImage"){
simpleImageResetNode(node, "circularImage", "node");
} else {
simpleResetNode(node, "node");
}
// finally, get back label
if (node.hiddenLabel !== undefined) {
node.label = node.hiddenLabel;
node.hiddenLabel = undefined;
}
node.isHardToRead = false;
}
}
}
}
// Global function to reset all node
function resetAllNodes(nodes, update, options, network, all){
if(all === false){
var nodesToReset = nodes.get({
filter: function (item) {
return item.isHardToRead === true;
},
returnType :'Array'
});
} else {
var nodesToReset = nodes.get({returnType :'Array'});
}
var have_cluster_nodes = false;
var nodes_in_clusters;
if(network !== undefined){
nodes_in_clusters = network.body.modules.clustering.clusteredNodes;
if(Object.keys(nodes_in_clusters).length > 0){
have_cluster_nodes = true;
nodes_in_clusters = Object.keys(nodes_in_clusters);
} else {
nodes_in_clusters = [];
}
}
for (var i = 0; i < nodesToReset.length; i++) {
resetOneNode(nodesToReset[i], options, network, type = "node");
// reset coordinates
nodesToReset[i].x = undefined;
nodesToReset[i].y = undefined;
if(have_cluster_nodes){
if(indexOf.call(nodes_in_clusters, nodesToReset[i].id, true) > -1){
var tmp_cluster_id = network.clustering.findNode(nodesToReset[i].id);
// in case of multiple cluster...
for(var j = 0; j < (tmp_cluster_id.length-1); j++) {
resetOneCluster(network.body.nodes[tmp_cluster_id[j]], options, network);
}
}
}
}
if(update){
nodes.update(nodesToReset);
}
}
//--------------------------------------------
// functions to set nodes as hard to read
//--------------------------------------------
// for classic node
function simpleNodeAsHardToRead(node, hideColor1, hideColor2, type){
// classic nodes
if(type === "node"){
// saving color information (if we have)
if (node.hiddenColor === undefined && node.color !== hideColor1 && node.color !== hideColor2) {
node.hiddenColor = node.color;
}
// set "hard to read" color
node.color = hideColor1;
// reset and save label
if (node.hiddenLabel === undefined) {
node.hiddenLabel = node.label;
node.label = undefined;
}
// cluster
} else {
// saving color information (if we have)
if (node.options.hiddenColor === undefined && node.options.color !== hideColor1 && node.options.color !== hideColor2) {
node.setOptions({hiddenColor : node.options.color});
}
// set "hard to read" color
node.setOptions({color : hideColor1});
// reset and save label
if (node.options.hiddenLabel === undefined) {
node.setOptions({hiddenLabel : node.options.label});
node.setOptions({label : undefined});
}
}
}
// for icon node
function iconsNodeAsHardToRead(node, hideColor1, hideColor2, icon_color, type){
// classic nodes
if(type === "node"){
// individual information
if(node.icon !== undefined && node.icon !== null && node.icon !== {}){
node.iconDefined = true;
} else { // information in group : have to as individual
node.icon = {};
node.iconDefined = false;
}
// set "hard to read" color
node.icon.color = hideColor1;
node.hiddenColor = icon_color;
// for edges....saving color information (if we have)
if (node.hiddenColorForLabel === undefined && node.color !== hideColor1 && node.color !== hideColor2) {
node.hiddenColorForLabel = node.color;
}
// set "hard to read" color
node.color = hideColor1;
// reset and save label
if (node.hiddenLabel === undefined) {
node.hiddenLabel = node.label;
node.label = undefined;
}
} else {
// individual information
if(node.options.icon !== undefined && node.options.icon !== null && node.options.icon !== {}){
node.setOptions({iconDefined : true});
} else { // information in group : have to as individual
node.setOptions({iconDefined : false, icon:{}});
}
// set "hard to read" color
node.setOptions({hiddenColor : icon_color, icon:{color : hideColor1}});
// for edges....saving color information (if we have)
if (node.options.hiddenColorForLabel === undefined && node.options.color !== hideColor1 && node.options.color !== hideColor2) {
node.setOptions({hiddenColorForLabel : node.options.color});
}
// set "hard to read" color
node.setOptions({color : hideColor1});
// reset and save label
if (node.options.hiddenLabel === undefined) {
node.setOptions({hiddenLabel : node.options.label, label : undefined});
}
}
}
// for image node
function imageNodeAsHardToRead(node, imageType, hideColor1, hideColor2, type){
// classic nodes
if(type === "node"){
// saving color information (if we have)
if (node.hiddenColor === undefined && node.color !== hideColor1 && node.color !== hideColor2) {
node.hiddenColor = node.color;
}
// set "hard to read" color
node.color = hideColor1;
// reset and save label
if (node.hiddenLabel === undefined) {
node.hiddenLabel = node.label;
node.label = undefined;
}
// keep shape information, and set a new
if(imageType === "image"){
node.hiddenImage = imageType;
node.shape = "square";
}else if(imageType === "circularImage"){
node.hiddenImage = imageType;
node.shape = "dot";
}
} else {
// saving color information (if we have)
if (node.options.hiddenColor === undefined && node.options.color !== hideColor1 && node.options.color !== hideColor2) {
node.setOptions({hiddenColor : node.options.color});
}
// set "hard to read" color
node.setOptions({color : hideColor1});
// reset and save label
if (node.options.hiddenLabel === undefined) {
node.setOptions({hiddenLabel : node.options.label, label : undefined});
}
if(imageType === "image"){
node.setOptions({hiddenImage : "image", shape : "square"});
} else if(imageType === "circularImage"){
node.setOptions({hiddenImage : "circularImage", shape : "dot"});
}
node.hiddenImage = imageType;
}
}
// Global function to set one node as hard to read
function nodeAsHardToRead(node, options, hideColor1, hideColor2, network, type){
var final_shape;
var shape_group = false;
var is_group = false;
if(node.isHardToRead === false || node.isHardToRead === undefined){
// have a group information & a shape defined in group ?
if(node.group !== undefined && options.groups !== undefined){
if(options.groups[node.group] !== undefined){
is_group = true;
if(options.groups[node.group].shape !== undefined){
shape_group = true;
}
}
}
// have a group information & a shape defined in group ?
var shape_options = false;
if(options.nodes !== undefined){
if(options.nodes.shape !== undefined){
shape_options = true;
}
}
// set final shape (individual > group > global)
if(node.shape !== undefined){
final_shape = node.shape;
} else if(shape_group){
final_shape = options.groups[node.group].shape;
} else if(shape_options){
final_shape = options.nodes.shape;
}
// information save in body nodes
if(type === "node"){
node.bodyHiddenColor = clone(network.body.nodes[node.id].options.color);
} else {
node.setOptions({bodyHiddenColor : clone(network.body.nodes[node.id].options.color)});
}
// and call good function
if(final_shape === "icon"){
// find color for icon
var icon_color = "#2B7CE9";
var find_color = false;
// in nodes ?
if(node.icon !== undefined){
if(node.icon.color !== undefined){
icon_color = node.icon.color;
find_color = true;
}
}
// or in group ?
if(find_color === false && is_group && options.groups !== undefined && options.groups[node.group].icon !== undefined){
if(options.groups[node.group].icon.color !== undefined){
icon_color = options.groups[node.group].icon.color;
find_color = true;
}
}
// in global node ?
if(find_color === false && options.nodes.icon !== undefined){
if(options.nodes.icon.color !== undefined){
icon_color = options.nodes.icon.color;
}
}
iconsNodeAsHardToRead(node, hideColor1, hideColor2, icon_color, type);
} else if(final_shape === "image"){
imageNodeAsHardToRead(node, "image", hideColor1, hideColor2, type);
} else if(final_shape === "circularImage"){
imageNodeAsHardToRead(node, "circularImage", hideColor1, hideColor2, type);
} else {
simpleNodeAsHardToRead(node, hideColor1, hideColor2, type);
}
// finally set isHardToRead
if(type === "node"){
node.isHardToRead = true;
} else {
node.setOptions({isHardToRead : true});
}
// special case of just to label
} else if(node.isHardToRead === true && node.label !== undefined){
if(type === "node"){
node.hiddenLabel = node.label;
node.label = undefined;
} else {
node.setOptions({hiddenLabel : node.options.label, label : undefined})
}
}
}
//----------------------------------------------------------------
// Revrite HTMLWidgets.dataframeToD3() for passing custom
// properties directly in data.frame (color.background) for example
//----------------------------------------------------------------
function visNetworkdataframeToD3(df, type) {
// variables we have specially to control
/*var nodesctrl = ["color", "fixed", "font", "icon", "shadow", "scaling", "shapeProperties", "chosen", "heightConstraint", "image", "margin", "widthConstraint"];
var edgesctrl = ["color", "font", "arrows", "shadow", "smooth", "scaling", "chosen", "widthConstraint"];*/
var names = [];
var colnames = [];
var length;
var toctrl;
var ctrlname;
for (var name in df) {
if (df.hasOwnProperty(name))
colnames.push(name);
ctrlname = name.split(".");
if(ctrlname.length === 1){
names.push( new Array(name));
} else {
/*if(type === "nodes"){
toctrl = indexOf.call(nodesctrl, ctrlname[0], true);
} else if(type === "edges"){
toctrl = indexOf.call(edgesctrl, ctrlname[0], true);
}
if(toctrl > -1){*/
names.push(ctrlname);
/*} else {
names.push(new Array(name));
}*/
}
if (typeof(df[name]) !== "object" || typeof(df[name].length) === "undefined") {
throw new Error("All fields must be arrays");
} else if (typeof(length) !== "undefined" && length !== df[name].length) {
throw new Error("All fields must be arrays of the same length");
}
length = df[name].length;
}
var results = [];
var item;
for (var row = 0; row < length; row++) {
item = {};
for (var col = 0; col < names.length; col++) {
if(df[colnames[col]][row] !== null){
if(names[col].length === 1){
if(names[col][0] === "dashes"){
item[names[col]] = eval(df[colnames[col]][row]);
} else {
item[names[col]] = df[colnames[col]][row];
}
} else if(names[col].length === 2){
if(item[names[col][0]] === undefined){
item[names[col][0]] = {};
}
if(names[col][0] === "icon" && names[col][1] === "code"){
item[names[col][0]][names[col][1]] = JSON.parse( '"'+'\\u' + df[colnames[col]][row] + '"');
} else if(names[col][0] === "icon" && names[col][1] === "color"){
item.color = df[colnames[col]][row];
item[names[col][0]][names[col][1]] = df[colnames[col]][row];
} else if(names[col][0] === "icon" && names[col][1] === "face"){
if(df[colnames[col]][row] === "'Font Awesome 5 Free'"){
item.icon.weight = "bold";
}
item[names[col][0]][names[col][1]] = df[colnames[col]][row];
} else{
item[names[col][0]][names[col][1]] = df[colnames[col]][row];
}
} else if(names[col].length === 3){
if(item[names[col][0]] === undefined){
item[names[col][0]] = {};
}
if(item[names[col][0]][names[col][1]] === undefined){
item[names[col][0]][names[col][1]] = {};
}
item[names[col][0]][names[col][1]][names[col][2]] = df[colnames[col]][row];
} else if(names[col].length === 4){
if(item[names[col][0]] === undefined){
item[names[col][0]] = {};
}
if(item[names[col][0]][names[col][1]] === undefined){
item[names[col][0]][names[col][1]] = {};
}
if(item[names[col][0]][names[col][1]][names[col][2]] === undefined){
item[names[col][0]][names[col][1]][names[col][2]] = {};
}
item[names[col][0]][names[col][1]][names[col][2]][names[col][3]] = df[colnames[col]][row];
}
}
}
results.push(item);
}
return results;
}
//----------------------------------------------------------------
// Some utils functions
//----------------------------------------------------------------
//unique element in array
function uniqueArray(arr, exclude_cluster, network) {
var a = [];
for (var i=0, l=arr.length; i function searchNodes() { // Get the search input element var searchInput = document.getElementById("search-input"); // Get the value of the search input var searchValue = searchInput.value.toLowerCase(); // Get all the nodes in the network var nodes = document.getElementsByClassName("vis-node"); // Iterate over the nodes and check if the label matches the search value for (var i = 0; i < nodes.length; i++) { var node = nodes[i]; var label = node.getAttribute("data-label").toLowerCase(); // Check if the label contains the search value if (label.includes(searchValue)) { // Highlight or zoom into the matching node (implement your own logic here) node.style.backgroundColor = "yellow"; } else { // Reset the style of non-matching nodes (optional) node.style.backgroundColor = ""; } } }
if (a.indexOf(arr[i]) === -1 && arr[i] !== ''){
if(exclude_cluster === false){
a.push(arr[i]);
} else if(network.isCluster(arr[i]) === false){
a.push(arr[i]);
}
}
}
return a;
}
function uniqueShiny(arr) {
return arr.filter(function (value, index, self) {
return self.indexOf(value) === index;
});
};
// clone an object
function clone(obj) {
if(obj === null || typeof(obj) != 'object')
return obj;
var temp = new obj.constructor();
for(var key in obj)
temp[key] = clone(obj[key]);
return temp;
}
// update a list
function update(source, target) {
Object.keys(target).forEach(function (k) {
if (typeof target[k] === 'object' && k !== "container") {
source[k] = source[k] || {};
update(source[k], target[k]);
} else {
source[k] = target[k];
}
});
}
// for find element
function indexOf(needle, str) {
indexOf = function(needle, str) {
var i = -1, index = -1;
if(str){
needle = ''+needle;
}
for(i = 0; i < this.length; i++) {
var val = this[i];
if(str){
val = ''+val;
}
if(val === needle) {
index = i;
break;
}
}
return index;
};
return indexOf.call(this, needle, str);
};
// reset a html list
function resetList(list_name, id, shiny_input_name) {
var list = document.getElementById(list_name + id);
list.value = "";
if (window.Shiny){
Shiny.onInputChange(id + '_' + shiny_input_name, "");
}
}
// id node list selection init
function setNodeIdList(selectList, params, nodes){
if(params.style !== undefined){
selectList.setAttribute('style', params.style);
}
selectList.style.display = 'inline';
option = document.createElement("option");
option.value = "";
if(params.main === undefined){
option.text = "Select by id";
} else {
option.text = params.main;
}
selectList.appendChild(option);
// have to set for all nodes ?
if(params.values === undefined){
var info_node_list = nodes.get({
fields: ['id', 'label'],
returnType :'Array'
});
for (var i = 0; i < info_node_list.length; i++) {
option = document.createElement("option");
option.value = info_node_list[i].id;
if(info_node_list[i].label && params.useLabels){
option.text = info_node_list[i].label;
}else{
option.text = info_node_list[i].id;
}
selectList.appendChild(option);
}
} else {
var tmp_node;
for(var tmp_id = 0 ; tmp_id < params.values.length; tmp_id++){
tmp_node = nodes.get({
fields: ['id', 'label'],
filter: function (item) {
return (item.id === params.values[tmp_id]) ;
},
returnType :'Array'
});
if(tmp_node !== undefined){
option = document.createElement("option");
option.value = tmp_node[0].id;
if(tmp_node[0].label && params.useLabels){
option.text = tmp_node[0].label;
}else{
option.text = tmp_node[0].id;
}
selectList.appendChild(option);
}
}
}
}
//----------------------------------------------------------------
// Collapsed function
//----------------------------------------------------------------
function networkOpenCluster(params){
if (params.nodes.length === 1) {
if (this.isCluster(params.nodes[0]) === true) {
var elid = this.body.container.id.substring(5);
var fit = document.getElementById(elid).collapseFit;
var resetHighlight = document.getElementById(elid).collapseResetHighlight;
if(document.getElementById(elid).collapseKeepCoord){
this.openCluster(params.nodes[0],
{releaseFunction : function(clusterPosition, containedNodesPositions) {
return containedNodesPositions;
}
});
} else {
this.openCluster(params.nodes[0]);
}
if(resetHighlight){
document.getElementById("nodeSelect"+elid).value = "";
document.getElementById("nodeSelect"+elid).onchange();
}
if(fit){
this.fit();
}
}
}
}
function collapsedNetwork(nodes, fit, resetHighlight, clusterParams, labelSuffix, treeParams, network, elid) {
var set_position = true;
var selectedNode;
var j;
if(nodes[0] !== undefined){
for (var inodes = 0; inodes < nodes.length; inodes++) {
selectedNode = nodes[inodes];
if(selectedNode !== undefined){
if(network.isCluster(selectedNode)){
//network.openCluster(selectedNode)
/*instance.network.openCluster(selectedNode,
{releaseFunction : function(clusterPosition, containedNodesPositions) {
return tmp_position;
}})*/
//networkOpenCluster(selectedNode)
} else {
var firstLevelNodes = [];
var otherLevelNodes = [];
var connectedToNodes = [];
item = network.body.data.nodes.get({
filter: function (item) {
return item.id == selectedNode;
}
});
connectedToNodes = network.body.data.edges.get({
fields: ['id','to'],
filter: function (item) {
return item.from == selectedNode;
},
returnType :'Array'
});
for (j = 0; j < connectedToNodes.length; j++) {
firstLevelNodes = firstLevelNodes.concat(connectedToNodes[j].to);
}
var currentConnectedToNodes = firstLevelNodes;
while(currentConnectedToNodes.length !== 0){
connectedToNodes = network.body.data.edges.get({
fields: ['id', 'to'],
filter: function (item) {
return indexOf.call(currentConnectedToNodes, item.from, true) > -1;
},
returnType :'Array'
});
currentConnectedToNodes = [];
var currentlength = otherLevelNodes.length;
for (j = 0; j < connectedToNodes.length; j++) {
otherLevelNodes = uniqueArray(otherLevelNodes.concat(connectedToNodes[j].to), false, network);
currentConnectedToNodes = uniqueArray(currentConnectedToNodes.concat(connectedToNodes[j].to), false, network);
}
if (otherLevelNodes.length === currentlength) { break; }
}
var finalFirstLevelNodes = [];
for (j = 0; j < firstLevelNodes.length; j++) {
var findnode = network.clustering.findNode(firstLevelNodes[j])
if(findnode.length === 1){
finalFirstLevelNodes = finalFirstLevelNodes.concat(firstLevelNodes[j]);
} else {
finalFirstLevelNodes = finalFirstLevelNodes.concat(findnode[0]);
}
}
var finalClusterNodes = [];
for (j = 0; j < otherLevelNodes.length; j++) {
var findnode = network.clustering.findNode(otherLevelNodes[j])
if(findnode.length === 1){
finalClusterNodes = finalClusterNodes.concat(otherLevelNodes[j]);
} else {
finalClusterNodes = finalClusterNodes.concat(findnode[0]);
}
}
if(set_position){
network.storePositions();
}
var clusterOptions = {
joinCondition: function (nodesOptions) {
return nodesOptions.id === selectedNode || indexOf.call(finalFirstLevelNodes, nodesOptions.id, true) > -1 ||
indexOf.call(finalClusterNodes, nodesOptions.id, true) > -1;
},
processProperties: function(clusterOptions, childNodes) {
var click_node = network.body.data.nodes.get({
filter: function (item) {
return item.id == selectedNode;
},
returnType :'Array'
});
var is_hard_to_read = false;
if(click_node[0].isHardToRead !== undefined){
is_hard_to_read = click_node[0].isHardToRead;
}
for (var i in click_node[0]) {
if(i !== "id" && i !== "isHardToRead"){
if(i === "label" && is_hard_to_read){
clusterOptions[i]= click_node[0]["hiddenLabel"];
} else if(i === "color" && is_hard_to_read) {
clusterOptions[i]= click_node[0]["hiddenColor"];
} else {
clusterOptions[i]= click_node[0][i];
}
}
}
// gestion des tree
if(treeParams !== undefined){
if(treeParams.updateShape){
clusterOptions.label = clusterOptions.labelClust
clusterOptions.color = clusterOptions.colorClust
clusterOptions.shape = treeParams.shapeY
}
}
if(clusterOptions.label !== undefined){
clusterOptions.label = clusterOptions.label + " " + labelSuffix;
} else {
clusterOptions.label = labelSuffix;
}
if(clusterOptions.borderWidth !== undefined){
clusterOptions.borderWidth = clusterOptions.borderWidth * 3;
} else {
clusterOptions.borderWidth = 3;
}
if(set_position){
if(click_node[0].x !== undefined){
clusterOptions.x = click_node[0].x;
}
if(click_node[0].y !== undefined){
clusterOptions.y = click_node[0].y;
}
}
if(clusterParams !== undefined){
for (var j in clusterParams) {
clusterOptions[j]= clusterParams[j];
}
}
return clusterOptions;
},
clusterNodeProperties: {
allowSingleNodeCluster: false
}
}
network.cluster(clusterOptions);
}
}
}
if(resetHighlight){
document.getElementById("nodeSelect"+elid).value = "";
document.getElementById("nodeSelect"+elid).onchange();
}
if(fit){
network.fit();
}
}
};
function uncollapsedNetwork(nodes, fit, resetHighlight, keepCoord, network, elid) {
var selectedNode;
var j;
var arr_nodes = [];
var cluster_node;
var nodes_in_clusters = network.body.modules.clustering.clusteredNodes;
if(Object.keys(nodes_in_clusters).length > 0){
nodes_in_clusters = Object.keys(nodes_in_clusters);
} else {
nodes_in_clusters = []
}
if(nodes !== undefined && nodes !== null){
arr_nodes = nodes
} else {
arr_nodes = nodes_in_clusters;
}
for (var inodes = 0; inodes < arr_nodes.length; inodes++) {
selectedNode = '' + arr_nodes[inodes];
if(selectedNode !== undefined){
if(network.isCluster(selectedNode)){
if(keepCoord){
network.openCluster(selectedNode,
{releaseFunction : function(clusterPosition, containedNodesPositions) {
return containedNodesPositions;
}
});
} else {
network.openCluster(selectedNode)
}
} else {
if(indexOf.call(nodes_in_clusters, selectedNode, true) > -1){
// not a cluster into a cluster...
if(selectedNode.search(/^cluster/i) === -1){
cluster_node = network.clustering.findNode(selectedNode)[0];
if(network.isCluster(cluster_node)){
if(keepCoord){
network.openCluster(cluster_node,
{releaseFunction : function(clusterPosition, containedNodesPositions) {
return containedNodesPositions;
}
});
} else {
network.openCluster(cluster_node)
}
}
}
}
}
}
}
if(resetHighlight){
document.getElementById("nodeSelect"+elid).value = "";
document.getElementById("nodeSelect"+elid).onchange();
}
if(fit){
network.fit();
}
};
//----------------------------------------------------------------
// All available functions/methods with visNetworkProxy
//---------------------------------------------------------------
if (HTMLWidgets.shinyMode){
// collapsed method
Shiny.addCustomMessageHandler('visShinyCollapse', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
collapsedNetwork(data.nodes, data.fit, data.resetHighlight, data.clusterOptions, data.labelSuffix, undefined, el.chart, data.id)
}
});
// uncollapsed method
Shiny.addCustomMessageHandler('visShinyUncollapse', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
uncollapsedNetwork(data.nodes, data.fit, data.resetHighlight, data.keepCoord, el.chart, data.id)
}
});
// event method
Shiny.addCustomMessageHandler('visShinyEvents', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
var network = el.chart;
if(data.type === "once"){
for (var key in data.events) {
eval('network.once("' + key + '",' + data.events[key] + ')');
}
} else if(data.type === "on"){
for (var key in data.events) {
eval('network.on("' + key + '",' + data.events[key] + ')');
}
} else if(data.type === "off"){
for (var key in data.events) {
eval('network.off("' + key + '",' + data.events[key] + ')');
}
}
}
});
// moveNode method
Shiny.addCustomMessageHandler('visShinyMoveNode', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
var network = el.chart;
network.moveNode(data.nodeId, data.x, data.y);
}
});
// unselectAll method
Shiny.addCustomMessageHandler('visShinyUnselectAll', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
var network = el.chart;
// reset selection
document.getElementById("nodeSelect"+data.id).value = "";
document.getElementById("nodeSelect"+data.id).onchange();
if(document.getElementById(data.id).selectActive === true){
document.getElementById("selectedBy"+data.id).value = "";
document.getElementById("selectedBy"+data.id).onchange();
}
network.unselectAll();
}
});
// updateOptions in the network
Shiny.addCustomMessageHandler('visShinyOptions', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
var network = el.chart;
var options = el.options;
// configure
if(data.options.configure !== undefined){
if(data.options.configure.container !== undefined){
var dom_conf = document.getElementById(data.options.configure.container);
if(dom_conf !== null){
data.options.configure.container = dom_conf;
} else {
data.options.configure.container = undefined;
}
}
}
//*************************
// pre-treatment for icons (unicode)
//*************************
if(data.options.groups){
for (var gr in data.options.groups){
if(data.options.groups[gr].icon){
if(data.options.groups[gr].icon.code){
data.options.groups[gr].icon.code = JSON.parse( '"'+'\\u' + data.options.groups[gr].icon.code + '"');
}
if(data.options.groups[gr].icon.face){
if(data.options.groups[gr].icon.face === "'Font Awesome 5 Free'"){
data.options.groups[gr].icon.weight = "bold"
}
}
if(data.options.groups[gr].icon.color){
data.options.groups[gr].color = data.options.groups[gr].icon.color;
}
}
}
}
if(data.options.nodes){
if(data.options.nodes.icon){
if(data.options.nodes.icon.code){
data.options.nodes.icon.code = JSON.parse( '"'+'\\u' + data.options.nodes.icon.code + '"');
}
if(data.options.nodes.icon.face){
if(data.options.nodes.icon.face === "'Font Awesome 5 Free'"){
data.options.nodes.icon.weight = "bold"
}
}
if(data.options.nodes.icon.color){
data.options.nodes.color = data.options.nodes.icon.color;
}
}
}
update(options, data.options);
network.setOptions(options);
}
});
// setData the network
Shiny.addCustomMessageHandler('visShinySetData', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
var network = el.chart;
var newnodes = new vis.DataSet();
var newedges = new vis.DataSet();
newnodes.add(visNetworkdataframeToD3(data.nodes, "nodes"));
newedges.add(visNetworkdataframeToD3(data.edges, "edges"));
var newdata = {
nodes: newnodes,
edges: newedges
};
network.setData(newdata);
}
});
// fit to a specific node
Shiny.addCustomMessageHandler('visShinyFit', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
var network = el.chart;
network.fit(data.options);
}
});
// focus on a node in the network
Shiny.addCustomMessageHandler('visShinyFocus', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
var network = el.chart;
network.focus(data.focusId, data.options);
}
});
// stabilize the network
Shiny.addCustomMessageHandler('visShinyStabilize', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
var network = el.chart;
network.stabilize(data.options);
}
});
// startSimulation on network
Shiny.addCustomMessageHandler('visShinyStartSimulation', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
var network = el.chart;
network.startSimulation();
}
});
// stopSimulation on network
Shiny.addCustomMessageHandler('visShinyStopSimulation', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
var network = el.chart;
network.stopSimulation();
}
});
// get positions of the network
Shiny.addCustomMessageHandler('visShinyGetPositions', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
var network = el.chart;
var pos;
if(data.nodes !== undefined){
pos = network.getPositions(data.nodes);
}else{
pos = network.getPositions();
}
// return positions in shiny
Shiny.onInputChange(data.input, pos);
}
});
// get edges data
Shiny.addCustomMessageHandler('visShinyGetEdges', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
// return data in shiny
Shiny.onInputChange(data.input, el.edges.get({returnType:"Object"}));
}
});
// get nodes data
Shiny.addCustomMessageHandler('visShinyGetNodes', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
if(data.addCoordinates){
el.chart.storePositions();
}
// return data in shiny
Shiny.onInputChange(data.input, el.nodes.get({returnType:"Object"}));
}
});
// get selected edges
Shiny.addCustomMessageHandler('visShinyGetSelectedEdges', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
var network = el.chart;
var pos = network.getSelectedEdges();
// return in shiny
Shiny.onInputChange(data.input, pos);
}
});
// get selected nodes
Shiny.addCustomMessageHandler('visShinyGetSelectedNodes', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
var network = el.chart;
var pos = network.getSelectedNodes();
// return in shiny
Shiny.onInputChange(data.input, pos);
}
});
// getConnectedEdges
Shiny.addCustomMessageHandler('visShinyGetConnectedEdges', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
var network = el.chart;
var pos = network.getConnectedEdges(data.nodeId);
// return in shiny
Shiny.onInputChange(data.input, pos);
}
});
// getConnectedNodes
Shiny.addCustomMessageHandler('visShinyGetConnectedNodes', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
var network = el.chart;
var pos = network.getConnectedNodes(data.nodeId);
// return in shiny
Shiny.onInputChange(data.input, pos);
}
});
// getBoundingBox
Shiny.addCustomMessageHandler('visShinyGetBoundingBox', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
var network = el.chart;
var pos = network.getBoundingBox(data.nodeId);
// return in shiny
Shiny.onInputChange(data.input, pos);
}
});
// get selection
Shiny.addCustomMessageHandler('visShinyGetSelection', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
var network = el.chart;
var pos;
pos = network.getSelection();
// return in shiny
Shiny.onInputChange(data.input, pos);
}
});
// get scale
Shiny.addCustomMessageHandler('visShinyGetScale', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
var network = el.chart;
var pos;
pos = network.getScale();
// return in shiny
Shiny.onInputChange(data.input, pos);
}
});
// store positions
Shiny.addCustomMessageHandler('visShinyStorePositions', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
var network = el.chart;
network.storePositions();
}
});
// get view position
Shiny.addCustomMessageHandler('visShinyGetViewPosition', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
var network = el.chart;
var pos;
pos = network.getViewPosition();
// return in shiny
Shiny.onInputChange(data.input, pos);
}
});
// get view position
Shiny.addCustomMessageHandler('visShinyGetOptionsFromConfigurator', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
var network = el.chart;
// return in shiny
Shiny.onInputChange(data.input, network.getOptionsFromConfigurator());
}
});
// Redraw the network
Shiny.addCustomMessageHandler('visShinyRedraw', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
el.chart.redraw();
}
});
// select nodes
Shiny.addCustomMessageHandler('visShinySelectNodes', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
var network = el.chart;
if(data.selid !== null){
network.selectNodes(data.selid, data.highlightEdges);
if(data.clickEvent){
el.myclick({nodes : data.selid});
}
}else{
if(data.clickEvent){
el.myclick({nodes : []});
}
}
}
});
// select edges
Shiny.addCustomMessageHandler('visShinySelectEdges', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
var network = el.chart;
if(data.selid !== null){
network.selectEdges(data.selid);
}
}
});
// set selection
Shiny.addCustomMessageHandler('visShinySetSelection', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(el){
var network = el.chart;
if(data.selection.nodes !== null || data.selection.edges !== null){
network.setSelection(data.selection, data.options);
}
if(data.clickEvent){
if(data.selection.nodes !== null){
el.myclick({nodes : data.selection.nodes});
} else {
el.myclick({nodes : []});
}
}
}
});
function updateVisOptions(data){
// get container id
var graph = document.getElementById("graph"+data.id);
var el = document.getElementById(data.id);
var do_loop_by = false;
var option2;
var selectList2;
var selectList;
var reset = false;
if(graph){
// reset nodes before ?
if(document.getElementById(el.id).highlight){
// need reset nodes
if(document.getElementById(el.id).highlightActive === true){
reset = true;
}
}
if(reset){
document.getElementById("nodeSelect"+data.id).value = "";
document.getElementById("nodeSelect"+data.id).onchange();
}
// collapse init
if(data.options.collapse !== undefined){
el.collapse = data.options.collapse.enabled;
el.collapseFit = data.options.collapse.fit;
el.collapseResetHighlight = data.options.collapse.resetHighlight;
el.collapseKeepCoord = data.options.collapse.keepCoord;
el.collapseLabelSuffix = data.options.collapse.labelSuffix;
el.clusterOptions = data.options.collapse.clusterOptions;
}
// highlight init
if(data.options.highlight !== undefined){
el.highlight = data.options.highlight.enabled;
el.degree = data.options.highlight.degree;
el.hoverNearest = data.options.highlight.hoverNearest;
el.highlightColor = data.options.highlight.hideColor;
el.highlightAlgorithm = data.options.highlight.algorithm;
el.highlightLabelOnly = data.options.labelOnly;
}
// byselection init
if(data.options.byselection !== undefined){
if(data.options.byselection.selected !== undefined){
document.getElementById("selectedBy"+data.id).value = data.options.byselection.selected;
document.getElementById("selectedBy"+data.id).onchange();
}
if(data.options.byselection.hideColor){
el.byselectionColor = data.options.byselection.hideColor;
}
if(data.options.byselection.highlight !== undefined){
el.byselectionHighlight = data.options.byselection.highlight;
}
}
if(data.options.byselection !== undefined){
selectList2 = document.getElementById("selectedBy"+data.id)
selectList2.options.length = 0;
if(data.options.byselection.enabled === true){
option2 = document.createElement("option");
option2.value = "";
if(data.options.byselection.main === undefined){
option2.text = "Select by " + data.options.byselection.variable;
} else {
option2.text = data.options.byselection.main;
}
selectList2.appendChild(option2);
if(data.options.byselection.values !== undefined){
for (var i = 0; i < data.options.byselection.values.length; i++) {
option2 = document.createElement("option");
option2.value = data.options.byselection.values[i];
option2.text = data.options.byselection.values[i];
selectList2.appendChild(option2);
}
}else{
do_loop_by = true;
}
el.byselection_variable = data.options.byselection.variable;
el.byselection_multiple = data.options.byselection.multiple;
selectList2.style.display = 'inline';
if(data.options.byselection.style !== undefined){
selectList2.setAttribute('style', data.options.byselection.style);
}
el.byselection = true;
} else {
selectList2.style.display = 'none';
el.byselection = false;
// reset selection
if(el.selectActive === true){
document.getElementById("selectedBy"+data.id).value = "";
document.getElementById("selectedBy"+data.id).onchange();
}
}
}else{
// reset selection
if(el.selectActive === true){
document.getElementById("selectedBy"+data.id).value = "";
document.getElementById("selectedBy"+data.id).onchange();
}
}
if(do_loop_by){
var allNodes = graph.nodes.get({returnType:"Object"});
var byselection_values = [];
for (var nodeId in allNodes) {
if(do_loop_by){
var current_sel_value = allNodes[nodeId][data.options.byselection.variable];
if(data.options.byselection.multiple){
current_sel_value = current_sel_value.split(",").map(Function.prototype.call, String.prototype.trim);
}else{
current_sel_value = [current_sel_value];
}
for(var ind_c in current_sel_value){
if(indexOf.call(byselection_values, current_sel_value[ind_c], false) === -1){
option2 = document.createElement("option");
option2.value = current_sel_value[ind_c];
option2.text = current_sel_value[ind_c];
selectList2.appendChild(option2);
byselection_values.push(current_sel_value[ind_c]);
}
}
}
}
}
// node id selection init
if(data.options.idselection !== undefined){
selectList = document.getElementById("nodeSelect"+data.id)
selectList.options.length = 0;
if(data.options.idselection.enabled === true){
setNodeIdList(selectList, data.options.idselection, graph.nodes)
el.idselection = true;
} else {
selectList.style.display = 'none';
el.idselection = false;
}
if(data.options.idselection.useLabels !== undefined){
el.idselection_useLabels = data.options.idselection.useLabels
}
}
if(data.options.idselection !== undefined){
if(data.options.idselection.enabled === true && data.options.idselection.selected !== undefined){
document.getElementById("nodeSelect"+data.id).value = data.options.idselection.selected;
document.getElementById("nodeSelect"+data.id).onchange();
}
}
}
};
Shiny.addCustomMessageHandler('visShinyCustomOptions', updateVisOptions);
// udpate nodes data
Shiny.addCustomMessageHandler('visShinyUpdateNodes', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
var main_el = document.getElementById(data.id);
if(data.legend === false){
if(el){
// get & transform nodes object
var tmpnodes = visNetworkdataframeToD3(data.nodes, "nodes");
// reset some parameters / data before
if (main_el.selectActive === true | main_el.highlightActive === true) {
//reset nodes
resetAllEdges(el.edges, el.highlightColor, el.byselectionColor, el.chart);
resetAllNodes(el.nodes, true, el.options, el.chart, false);
if (main_el.selectActive === true){
main_el.selectActive = false;
resetList('selectedBy', data.id, 'selectedBy');
}
if (main_el.highlightActive === true){
main_el.highlightActive = false;
resetList('nodeSelect', data.id, 'selected');
}
}
// update nodes
el.nodes.update(tmpnodes);
// update options ?
if(data.updateOptions){
var dataOptions = {};
dataOptions.options = {};
var updateOpts = false;
if(document.getElementById("nodeSelect"+data.id).style.display === 'inline'){
updateOpts = true;
dataOptions.id = data.id;
dataOptions.options.idselection = {enabled : true, useLabels : main_el.idselection_useLabels};
}
if(document.getElementById("selectedBy"+data.id).style.display === 'inline'){
updateOpts = true;
dataOptions.id = data.id;
dataOptions.options.byselection = {enabled : true, variable : main_el.byselection_variable, multiple : main_el.byselection_multiple};
}
if(updateOpts){
updateVisOptions(dataOptions);
}
}
}
} else if(data.legend === true){
var legend_network = document.getElementById("legend"+data.id);
if(legend_network){
// get & transform nodes object
var tmpnodes = visNetworkdataframeToD3(data.nodes, "nodes");
// update nodes
legend_network.network.body.data.nodes.update(tmpnodes);
// fit
legend_network.network.fit();
}
}
});
// udpate edges data
Shiny.addCustomMessageHandler('visShinyUpdateEdges', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(data.legend === false){
if(el){
// get edges object
var tmpedges = visNetworkdataframeToD3(data.edges, "edges");
// reset edges
resetAllEdges(el.edges, el.highlightColor, el.byselectionColor, el.chart)
el.edges.update(tmpedges);
}
} else if(data.legend === true){
var legend_network = document.getElementById("legend"+data.id);
if(legend_network){
// get & transform nodes object
var tmpedges = visNetworkdataframeToD3(data.edges, "edges");
// update edges
legend_network.network.body.data.edges.update(tmpedges);
// fit
legend_network.network.fit();
}
}
});
// remove nodes
Shiny.addCustomMessageHandler('visShinyRemoveNodes', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
var main_el = document.getElementById(data.id);
if(data.legend === false){
if(el){
// reset some parameters / date before
if (main_el.selectActive === true | main_el.highlightActive === true) {
//reset nodes
resetAllNodes(el.nodes, true, el.options, el.chart, false);
if (main_el.selectActive === true){
main_el.selectActive = false;
resetList('selectedBy', data.id, 'selectedBy');
}
if (main_el.highlightActive === true){
main_el.highlightActive = false;
resetList('nodeSelect', data.id, 'selected');
}
}
// remove nodes
el.nodes.remove(data.rmid);
// update options ?
if(data.updateOptions){
var dataOptions = {};
dataOptions.options = {};
var updateOpts = false;
if(document.getElementById("nodeSelect"+data.id).style.display === 'inline'){
updateOpts = true;
dataOptions.id = data.id;
dataOptions.options.idselection = {enabled : true, useLabels : main_el.idselection_useLabels};
}
if(document.getElementById("selectedBy"+data.id).style.display === 'inline'){
updateOpts = true;
dataOptions.id = data.id;
dataOptions.options.byselection = {enabled : true, variable : main_el.byselection_variable, multiple : main_el.byselection_multiple};
}
if(updateOpts){
updateVisOptions(dataOptions);
}
}
}
} else if(data.legend === true){
var legend_network = document.getElementById("legend"+data.id);
if(legend_network){
// remove nodes
legend_network.network.body.data.nodes.remove(data.rmid);
// fit
legend_network.network.fit();
}
}
});
// remove edges
Shiny.addCustomMessageHandler('visShinyRemoveEdges', function(data){
// get container id
var el = document.getElementById("graph"+data.id);
if(data.legend === false){
if(el){
// reset edges
resetAllEdges(el.edges, el.highlightColor, el.byselectionColor, el.chart)
el.edges.remove(data.rmid);
}
} else if(data.legend === true){
var legend_network = document.getElementById("legend"+data.id);
if(legend_network){
// remove edges
legend_network.network.body.data.edges.remove(data.rmid);
// fit
legend_network.network.fit();
}
}
});
// remove edges
Shiny.addCustomMessageHandler('visShinySetTitle', function(data){
if(data.main !== null){
var div_title = document.getElementById("title" + data.id);
if(div_title !== null){
if(data.main.hidden === true){
div_title.style.display = 'none';
} else {
if(data.main.text !== undefined){
if(data.main.text !== null){
if(data.main.text.length > 0){
div_title.innerHTML = data.main.text;
} else {
div_title.innerHTML = "";
}
}
}
if(data.main.style !== undefined){
if(data.main.style !== null){
if(data.main.style.length > 0){
div_title.setAttribute('style', data.main.style);
}
}
}
div_title.style.display = 'block';
}
}
}
if(data.submain !== null){
var div_subtitle = document.getElementById("subtitle" + data.id);
if(div_subtitle !== null){
if(data.submain.hidden === true){
div_subtitle.style.display = 'none';
} else {
if(data.submain.text !== undefined){
if(data.submain.text !== null){
if(data.submain.text.length > 0){
div_subtitle.innerHTML = data.submain.text;
} else {
div_subtitle.innerHTML = "";
}
}
}
if(data.submain.style !== undefined){
if(data.submain.style !== null){
if(data.submain.style.length > 0){
div_subtitle.setAttribute('style', data.submain.style);
}
}
}
div_subtitle.style.display = 'block';
}
}
}
if(data.footer !== null){
var div_footer = document.getElementById("footer" + data.id);
if(div_footer !== null){
if(data.footer.hidden === true){
div_footer.style.display = 'none';
} else {
if(data.footer.text !== undefined){
if(data.footer.text !== null){
if(data.footer.text.length > 0){
div_footer.innerHTML = data.footer.text;
} else {
div_footer.innerHTML = "";
}
}
}
if(data.footer.style !== undefined){
if(data.footer.style !== null){
if(data.footer.style.length > 0){
div_footer.setAttribute('style', data.footer.style);
}
}
}
div_footer.style.display = 'block';
}
}
}
});
// updateTree
Shiny.addCustomMessageHandler('visShinyUpdateTree', function(data){
// get container id
var el = document.getElementById(data.id);
if(el){
if(el.tree){
if(data.tree.updateShape != undefined){
el.tree.updateShape = data.tree.updateShape
}
if(data.tree.shapeVar != undefined){
el.tree.shapeVar = data.tree.shapeVar
}
if(data.tree.shapeY != undefined){
el.tree.shapeY = data.tree.shapeY
}
}
}
});
}
//----------------------------------------------------------------
// HTMLWidgets.widget Definition
//---------------------------------------------------------------
HTMLWidgets.widget({
name: 'visNetwork',
type: 'output',
initialize: function(el, width, height) {
return {
};
},
renderValue: function(el, x, instance) {
var data;
var nodes;
var edges;
// clustergin by zoom variables
var clusterIndex = 0;
var clusters = [];
var lastClusterZoomLevel = 0;
var clusterFactor;
var ctrlwait = 0;
// legend control
var addlegend = false;
// main div el.id
var el_id = document.getElementById(el.id);
// test background
el_id.style.background = x.background;
// clear el.id (for shiny...)
el_id.innerHTML = "";
// shared control with proxy function (is there a better way ?)
el_id.highlightActive = false;
el_id.selectActive = false;
el_id.idselection = x.idselection.enabled;
el_id.byselection = x.byselection.enabled;
if(x.highlight !== undefined){
el_id.highlight = x.highlight.enabled;
el_id.highlightColor = x.highlight.hideColor;
el_id.hoverNearest = x.highlight.hoverNearest;
el_id.degree = x.highlight.degree;
el_id.highlightAlgorithm = x.highlight.algorithm;
el_id.highlightLabelOnly = x.highlight.labelOnly;
} else {
el_id.highlight = false;
el_id.hoverNearest = false;
el_id.highlightColor = 'rgba(200,200,200,0.5)';
el_id.degree = 1;
el_id.highlightAlgorithm = "all";
el_id.highlightLabelOnly = true;
}
if(x.byselection.enabled){
el_id.byselectionColor = x.byselection.hideColor;
el_id.byselectionHighlight = x.byselection.highlight;
} else {
el_id.byselectionColor = 'rgba(200,200,200,0.5)';
el_id.byselectionHighlight = false;
}
if(x.idselection.enabled){
el_id.idselection_useLabels = true;
} else {
el_id.idselection_useLabels = false;
}
if(x.collapse !== undefined){
if(x.collapse.enabled){
el_id.collapse = true;
el_id.collapseFit = x.collapse.fit;
el_id.collapseResetHighlight = x.collapse.resetHighlight;
el_id.collapseKeepCoord = x.collapse.keepCoord;
el_id.collapseLabelSuffix = x.collapse.labelSuffix;
el_id.clusterOptions = x.collapse.clusterOptions;
}
} else {
el_id.collapse = false;
el_id.collapseFit = false;
el_id.collapseResetHighlight = false;
el_id.collapseKeepCoord = true;
el_id.collapseLabelSuffix = " (cluster)";
el_id.clusterOptions = undefined;
}
if(x.tree !== undefined){
el_id.tree = x.tree;
}
// configure
if(x.options.configure !== undefined){
if(x.options.configure.container !== undefined){
var dom_conf = document.getElementById(x.options.configure.container);
if(dom_conf !== null){
x.options.configure.container = dom_conf;
} else {
x.options.configure.container = undefined;
}
}
}
var changeInput = function(id, data) {
Shiny.onInputChange(el.id + '_' + id, data);
};
//*************************
//title
//*************************
var div_title = document.createElement('div');
div_title.id = "title"+el.id;
div_title.setAttribute('style','font-family:Georgia, Times New Roman, Times, serif;font-weight:bold;font-size:20px;text-align:center;');
div_title.style.display = 'none';
el_id.appendChild(div_title);
if(x.main !== null){
div_title.innerHTML = x.main.text;
div_title.setAttribute('style', x.main.style + ";background-color: inherit;");
div_title.style.display = 'block';
}
//*************************
//subtitle
//*************************
var div_subtitle = document.createElement('div');
div_subtitle.id = "subtitle"+el.id;
div_subtitle.setAttribute('style', 'font-family:Georgia, Times New Roman, Times, serif;font-size:12px;text-align:center;');
div_subtitle.style.display = 'none';
el_id.appendChild(div_subtitle);
if(x.submain !== null){
div_subtitle.innerHTML = x.submain.text;
div_subtitle.setAttribute('style', x.submain.style + ";background-color: inherit;");
div_title.style.display = 'block';
}
//*************************
//init idselection
//*************************
function onIdChange(id, init) {
if(id === ""){
instance.network.selectNodes([]);
}else{
instance.network.selectNodes([id]);
}
if(el_id.highlight){
neighbourhoodHighlight(instance.network.getSelection().nodes, "click", el_id.highlightAlgorithm, true);
}else{
if(init){
selectNode = document.getElementById('nodeSelect'+el.id);
if(x.idselection.values !== undefined){
if(indexOf.call(x.idselection.values, id, true) > -1){
selectNode.value = id;
}else{
selectNode.value = "";
}
}else{
selectNode.value = id;
}
}
}
if (window.Shiny){
changeInput('selected', document.getElementById("nodeSelect"+el.id).value);
}
if(el_id.byselection){
resetList('selectedBy', el.id, 'selectedBy');
}
}
// id nodes selection : add a list on top left
// actually only with nodes + edges data (not dot and gephi)
var idList = document.createElement("select");
idList.setAttribute('class', 'dropdown');
idList.style.display = 'none';
idList.id = "nodeSelect"+el.id;
el_id.appendChild(idList);
idList.onchange = function(){
if(instance.network){
onIdChange(document.getElementById("nodeSelect"+el.id).value, false);
}
};
var hr = document.createElement("hr");
hr.setAttribute('style', 'height:0px; visibility:hidden; margin-bottom:-1px;');
el_id.appendChild(hr);
//*************************
//selectedBy
//*************************
function onByChange(value) {
if(instance.network){
selectedHighlight(value);
}
if (window.Shiny){
changeInput('selectedBy', value);
}
if(el_id.idselection){
resetList('nodeSelect', el.id, 'selected');
}
}
// selectedBy : add a list on top left
// actually only with nodes + edges data (not dot and gephi)
//Create and append select list
var byList = document.createElement("select");
byList.setAttribute('class', 'dropdown');
byList.style.display = 'none';
byList.id = "selectedBy"+el.id;
el_id.appendChild(byList);
byList.onchange = function(){
onByChange(document.getElementById("selectedBy"+el.id).value);
};
if(el_id.byselection){
el_id.byselection_values = x.byselection.values;
el_id.byselection_variable = x.byselection.variable;
el_id.byselection_multiple = x.byselection.multiple;
var option2;
//Create and append select list
var selectList2 = document.getElementById("selectedBy"+el.id);
selectList2.setAttribute('style', x.byselection.style);
selectList2.style.display = 'inline';
option2 = document.createElement("option");
option2.value = "";
if(x.byselection.main === undefined){
option2.text = "Select by " + x.byselection.variable;
} else {
option2.text = x.byselection.main;
}
selectList2.appendChild(option2);
//Create and append the options
for (var i2 = 0; i2 < x.byselection.values.length; i2++) {
option2 = document.createElement("option");
option2.value = x.byselection.values[i2];
option2.text = x.byselection.values[i2];
selectList2.appendChild(option2);
}
if (window.Shiny){
changeInput('selectedBy', document.getElementById("selectedBy"+el.id).value);
}
}
//*************************
// pre-treatment for icons (unicode)
//*************************
if(x.options.groups){
for (var gr in x.options.groups){
if(x.options.groups[gr].icon){
if(x.options.groups[gr].icon.code){
x.options.groups[gr].icon.code = JSON.parse( '"'+'\\u' + x.options.groups[gr].icon.code + '"');
}
if(x.options.groups[gr].icon.face){
if(x.options.groups[gr].icon.face === "'Font Awesome 5 Free'"){
x.options.groups[gr].icon.weight = "bold"
}
}
if(x.options.groups[gr].icon.color){
x.options.groups[gr].color = x.options.groups[gr].icon.color;
}
}
}
}
if(x.options.nodes.icon){
if(x.options.nodes.icon.code){
x.options.nodes.icon.code = JSON.parse( '"'+'\\u' + x.options.nodes.icon.code + '"');
}
if(x.options.nodes.icon.face){
if(x.options.nodes.icon.face === "'Font Awesome 5 Free'"){
x.options.nodes.icon.weight = "bold"
}
}
if(x.options.nodes.icon.color){
x.options.nodes.color = x.options.nodes.icon.color;
}
}
//*************************
//page structure
//*************************
// divide page
var maindiv = document.createElement('div');
maindiv.id = "maindiv"+el.id;
maindiv.setAttribute('style', 'height:95%;background-color: inherit;');
el_id.appendChild(maindiv);
var graph = document.createElement('div');
graph.id = "graph"+el.id;
if(x.legend !== undefined){
if((x.groups && x.legend.useGroups) || (x.legend.nodes !== undefined) || (x.legend.edges !== undefined)){
addlegend = true;
}
}
//legend
if(addlegend){
var legendwidth = x.legend.width*100;
var legend = document.createElement('div');
var pos = x.legend.position;
var pos2 = "right";
if(pos == "right"){
pos2 = "left";
}
legend.id = "legend"+el.id;
legend.setAttribute('style', 'float:' + pos + '; width:'+legendwidth+'%;height:100%');
//legend title
if(x.legend.main !== undefined){
var legend_title = document.createElement('div');
legend_title.innerHTML = x.legend.main.text;
legend_title.setAttribute('style', x.legend.main.style);
legend.appendChild(legend_title);
legend.id = "legend_main"+el.id;
var legend_network = document.createElement('div');
legend_network.id = "legend"+el.id;
legend_network.setAttribute('style', 'height:100%');
legend.appendChild(legend_network);
}
document.getElementById("maindiv"+el.id).appendChild(legend);
graph.setAttribute('style', 'float:' + pos2 + '; width:'+(100-legendwidth)+'%;height:100%;background-color: inherit;');
}else{
graph.setAttribute('style', 'float:right; width:100%;height:100%;background-color: inherit;');
}
document.getElementById("maindiv"+el.id).appendChild(graph);
//*************************
//legend definition
//*************************
if(addlegend){
var legendnodes = new vis.DataSet();
var legendedges = null;
var datalegend;
var tmpnodes;
// set some options
var optionslegend = {
interaction:{
dragNodes: false,
dragView: false,
selectable: false,
zoomView: x.legend.zoom
},
physics:{
stabilization: false
}
};
function range(start, length, step, rep){
var a=[], b=start;
while(a.length < length){
for (var i = 0; i < rep; i++){
a.push(b);
if(a.length === length){
break;
}
}
b+=step;
}
return a;
};
var mynetwork = document.getElementById('legend'+el.id);
var lx = mynetwork.clientWidth / 2 + 50;
var ly = mynetwork.clientHeight / 2 + 50;
var edge_ly = ly;
var ncol = x.legend.ncol;
var step_x = x.legend.stepX;
var step_y = x.legend.stepY;
var tmp_ly;
var tmp_lx = lx;
var tmp_lx2;
var all_tmp_y = [];
if(tmp_lx === 0){
tmp_lx = 1
}
// construct nodes data if needed
if(x.legend.nodes !== undefined){
if(x.legend.nodesToDataframe){ // data in data.frame
tmpnodes = visNetworkdataframeToD3(x.legend.nodes, "nodes")
} else { // data in list
tmpnodes = x.legend.nodes;
}
// only one element
if(tmpnodes.length === undefined){
tmpnodes = new Array(tmpnodes);
}
}
// array of y position
if(x.groups && x.legend.useGroups && x.legend.nodes !== undefined){
all_tmp_y = range(ly, x.groups.length + tmpnodes.length, step_y, ncol);
} else if(x.groups && x.legend.useGroups && x.legend.nodes === undefined){
all_tmp_y = range(ly, x.groups.length, step_y, ncol);
} else if(x.legend.useGroups === false && x.legend.nodes !== undefined){
all_tmp_y = range(ly, tmpnodes.length, step_y, ncol);
}
// want to view groups in legend
if(x.groups && x.legend.useGroups){
// create data
for (var g1 = 0; g1 < x.groups.length; g1++){
if(g1 === 0){
tmp_lx = lx;
} else {
tmp_lx = lx + g1%ncol * step_x;
}
tmp_ly = all_tmp_y[g1];
if(tmp_ly === 0){
tmp_ly = 1
}
legendnodes.add({id: null, x : tmp_lx, y : tmp_ly, label: x.groups[g1], group: x.groups[g1], value: 1, mass:1});
edge_ly = tmp_ly;
}
// control icon size
if(x.options.groups){
optionslegend.groups = clone(x.options.groups);
for (var grp in optionslegend.groups) {
if(optionslegend.groups[grp].shape === "icon"){
optionslegend.groups[grp].icon.size = 50;
}
}
}
}
// want to add custom nodes
if(x.legend.nodes !== undefined){
// control icon
for (var nd in tmpnodes){
if(tmpnodes[nd].icon && !x.legend.nodesToDataframe){
tmpnodes[nd].icon.code = JSON.parse( '"'+'\\u' + tmpnodes[nd].icon.code + '"');
}
if(tmpnodes[nd].icon && tmpnodes[nd].icon.face){
if(tmpnodes[nd].icon.face === "'Font Awesome 5 Free'"){
tmpnodes[nd].icon.weight = "bold"
}
}
}
// group control for y
var add_gr_y = 0;
if(x.groups && x.legend.useGroups){
add_gr_y = x.groups.length;
}
// set coordinates
for (var g = 0; g < tmpnodes.length; g++){
if((g+legendnodes.length) === 0){
tmp_lx = lx;
} else {
tmp_lx = lx + (g+legendnodes.length)%ncol * step_x;
}
tmp_ly = all_tmp_y[add_gr_y + g];
if(tmp_lx === 0){
tmp_lx = 1
}
if(tmp_ly === 0){
tmp_ly = 1
}
tmpnodes[g].x = tmp_lx;
tmpnodes[g].y = tmp_ly;
if(tmpnodes[g].value === undefined && tmpnodes[g].size === undefined){
tmpnodes[g].value = 1;
}
/*if(tmpnodes[g].id !== undefined){
tmpnodes[g].id = null;
}*/
tmpnodes[g].mass = 1;
edge_ly = tmp_ly;
}
legendnodes.add(tmpnodes);
}
// want to add custom edges
if(x.legend.edges !== undefined){
if(x.legend.edgesToDataframe){ // data in data.frame
legendedges = visNetworkdataframeToD3(x.legend.edges, "edges")
} else { // data in list
legendedges = x.legend.edges;
}
// only one element
if(legendedges.length === undefined){
legendedges = new Array(legendedges);
}
// set coordinates and options
for (var edg = 0; edg < (legendedges.length); edg++){
var tmp_int = Math.floor(Math.random() * 1001);
legendedges[edg].from = edg + "tmp_leg_edges_" + tmp_int + "_1";
legendedges[edg].to = edg + "tmp_leg_edges_" + tmp_int + "_2";
legendedges[edg].physics = false;
legendedges[edg].smooth = false;
legendedges[edg].value = undefined;
if(legendedges[edg].arrows === undefined){
legendedges[edg].arrows = 'to';
}
if(legendedges[edg].width === undefined){
legendedges[edg].width = 1;
}
tmp_ly = edge_ly + (edg+1)*step_y;
if(tmp_ly === 0){
tmp_ly = 1
}
if(ncol === 1){
tmp_lx = lx - mynetwork.clientWidth/3;
tmp_lx2 = lx + mynetwork.clientWidth/3;
} else {
tmp_lx = lx;
tmp_lx2 = lx + (ncol-1) * step_x;
}
if(tmp_lx === 0){
tmp_lx = 1
}
if(tmp_lx2 === 0){
tmp_lx2 = 1
}
legendnodes.add({id: edg + "tmp_leg_edges_" + tmp_int + "_1", x : tmp_lx, y : tmp_ly, size : 0.0001, hidden : false, shape : "square", mass:1});
legendnodes.add({id: edg + "tmp_leg_edges_" + tmp_int + "_2", x : tmp_lx2, y : tmp_ly, size : 0.0001, hidden : false, shape : "square", mass:1});
}
}
// render legend network
datalegend = {
nodes: legendnodes,
edges: legendedges
};
instance.legend = new vis.Network(document.getElementById("legend"+el.id), datalegend, optionslegend);
//link network for update for re-use and update
document.getElementById("legend"+el.id).network = instance.legend;
}
//*************************
// Main Network rendering
//*************************
if(x.nodes){
// network
nodes = new vis.DataSet();
edges = new vis.DataSet();
var tmpnodes;
if(x.nodesToDataframe){ // data in data.frame
tmpnodes = visNetworkdataframeToD3(x.nodes, "nodes")
} else { // data in list
tmpnodes = x.nodes;
}
// only one element
if(tmpnodes.length === undefined){
tmpnodes = new Array(tmpnodes);
}
// update coordinates if igraph
if(x.igraphlayout !== undefined){
// to improved
var zoomLevel = -232.622349 / (tmpnodes.length + 91.165919) +2.516861;
var igclientWidth = document.getElementById("graph"+el.id).clientWidth;
var scalex = 100;
var scaley = 100;
// current div visibled
if(igclientWidth !== 0){
var factor = igclientWidth / 1890;
zoomLevel = zoomLevel/factor;
var scalex = (igclientWidth / 2) * zoomLevel;
var scaley = scalex;
if(x.igraphlayout.type !== "square"){
scaley = (document.getElementById("graph"+el.id).clientHeight / 2) * zoomLevel;
}
} else {
// current div not visibled....
igclientWidth = parseInt(el_id.style.width);
if(igclientWidth !== 0){
var factor = igclientWidth / 1890;
zoomLevel = zoomLevel/factor;
var scalex = (igclientWidth / 2) * zoomLevel;
var scaley = scalex;
if(x.igraphlayout.type !== "square"){
scaley = (parseInt(el_id.style.height) / 2) * zoomLevel;
}
}
}
for (var nd in tmpnodes) {
tmpnodes[nd].x = tmpnodes[nd].x * scalex;
tmpnodes[nd].y = tmpnodes[nd].y * scaley;
}
}
nodes.add(tmpnodes);
var tmpedges;
if(x.edgesToDataframe){ // data in data.frame
tmpedges = visNetworkdataframeToD3(x.edges, "edges")
} else { // data in list
tmpedges = x.edges;
}
// only one element
if(tmpedges !== null){
if(tmpedges.length === undefined){
tmpedges = new Array(tmpedges);
}
edges.add(tmpedges);
}
// reset tmpnodes
tmpnodes = null;
data = {
nodes: nodes,
edges: edges
};
//save data for re-use and update
document.getElementById("graph"+el.id).nodes = nodes;
document.getElementById("graph"+el.id).edges = edges;
}else if(x.dot){
data = {
dot: x.dot
};
}else if(x.gephi){
data = {
gephi: x.gephi
};
}
var options = x.options;
//*************************
//manipulation
//*************************
if(x.options.manipulation.enabled){
var style = document.createElement('style');
style.type = 'text/css';
style.appendChild(document.createTextNode(x.opts_manipulation.datacss));
document.getElementsByTagName("head")[0].appendChild(style);
var div_addnode = document.createElement('div');
div_addnode.id = 'addnode-popUp';
div_addnode.classList.add('network-popUp');
div_addnode.innerHTML = x.opts_manipulation.tab_add_node;
el_id.appendChild(div_addnode);
var div_editnode = document.createElement('div');
div_editnode.id = 'editnode-popUp';
div_editnode.classList.add('network-popUp');
div_editnode.innerHTML = x.opts_manipulation.tab_edit_node;
el_id.appendChild(div_editnode);
var div_editedge = document.createElement('div');
div_editedge.id = 'editedge-popUp';
div_editedge.classList.add('network-popUp');
div_editedge.innerHTML = x.opts_manipulation.tab_edit_edge;
el_id.appendChild(div_editedge);
if(x.options.manipulation.addNode === undefined){
options.manipulation.addNode = function(data, callback) {
document.getElementById('addnode-operation').innerHTML = "Add Node";
for (var nodecol = 0; nodecol < x.opts_manipulation.addNodeCols.length; nodecol++){
document.getElementById('addnode-' + x.opts_manipulation.addNodeCols[nodecol]).value = data[x.opts_manipulation.addNodeCols[nodecol]];
}
document.getElementById('addnode-saveButton').onclick = saveNode.bind(this, data, callback, "addNode");
document.getElementById('addnode-cancelButton').onclick = clearPopUp.bind();
document.getElementById('addnode-popUp').style.display = 'block';
};
} else if(typeof(x.options.manipulation.addNode) === typeof(true)){
if(x.options.manipulation.addNode){
options.manipulation.addNode = function(data, callback) {
document.getElementById('addnode-operation').innerHTML = "Add Node";
for (var nodecol = 0; nodecol < x.opts_manipulation.addNodeCols.length; nodecol++){
document.getElementById('addnode-' + x.opts_manipulation.addNodeCols[nodecol]).value = data[x.opts_manipulation.addNodeCols[nodecol]];
}
document.getElementById('addnode-saveButton').onclick = saveNode.bind(this, data, callback, "addNode");
document.getElementById('addnode-cancelButton').onclick = clearPopUp.bind();
document.getElementById('addnode-popUp').style.display = 'block';
};
} else {
options.manipulation.addNode = false;
}
} else {
options.manipulation.addNode = x.options.manipulation.addNode;
}
if(x.options.manipulation.editNode === undefined){
options.manipulation.editNode = function(data, callback) {
var node_data = nodes.get(data.id);
document.getElementById('editnode-operation').innerHTML = "Edit Node";
for (var nodecol = 0; nodecol < x.opts_manipulation.editNodeCols.length; nodecol++){
document.getElementById('editnode-' + x.opts_manipulation.editNodeCols[nodecol]).value = node_data[x.opts_manipulation.editNodeCols[nodecol]];
}
document.getElementById('editnode-saveButton').onclick = saveNode.bind(this, data, callback, "editNode");
document.getElementById('editnode-cancelButton').onclick = cancelEdit.bind(this,callback);
document.getElementById('editnode-popUp').style.display = 'block';
};
} else if(typeof(x.options.manipulation.editNode) === typeof(true)){
if(x.options.manipulation.editNode){
options.manipulation.editNode = function(data, callback) {
var node_data = nodes.get(data.id);
document.getElementById('editnode-operation').innerHTML = "Edit Node";
for (var nodecol = 0; nodecol < x.opts_manipulation.editNodeCols.length; nodecol++){
document.getElementById('editnode-' + x.opts_manipulation.editNodeCols[nodecol]).value = node_data[x.opts_manipulation.editNodeCols[nodecol]];
}
document.getElementById('editnode-saveButton').onclick = saveNode.bind(this, data, callback, "editNode");
document.getElementById('editnode-cancelButton').onclick = cancelEdit.bind(this,callback);
document.getElementById('editnode-popUp').style.display = 'block';
};
} else {
options.manipulation.editNode = false;
}
} else {
options.manipulation.editNode = x.options.manipulation.editNode;
}
if(x.options.manipulation.deleteNode === undefined){
options.manipulation.deleteNode = function(data, callback) {
var r = confirm("Do you want to delete " + data.nodes.length + " node(s) and " + data.edges.length + " edges ?");
if (r === true) {
deleteSubGraph(data, callback);
} else { clearPopUp(); callback(null); }
};
} else if(typeof(x.options.manipulation.deleteNode) === typeof(true)){
if(x.options.manipulation.deleteNode){
options.manipulation.deleteNode = function(data, callback) {
var r = confirm("Do you want to delete " + data.nodes.length + " node(s) and " + data.edges.length + " edges ?");
if (r === true) {
deleteSubGraph(data, callback);
} else { clearPopUp(); callback(null); }
};
} else {
options.manipulation.deleteNode = false;
}
} else {
options.manipulation.deleteNode = x.options.manipulation.deleteNode;
}
if(x.options.manipulation.deleteEdge === undefined){
options.manipulation.deleteEdge = function(data, callback) {
var r = confirm("Do you want to delete " + data.edges.length + " edges ?");
if (r === true) {
deleteSubGraph(data, callback);
} else { clearPopUp(); callback(null); }
};
} else if(typeof(x.options.manipulation.deleteEdge) === typeof(true)){
if(x.options.manipulation.deleteEdge){
options.manipulation.deleteEdge = function(data, callback) {
var r = confirm("Do you want to delete " + data.edges.length + " edges ?");
if (r === true) {
deleteSubGraph(data, callback);
} else { clearPopUp(); callback(null); }
};
} else {
options.manipulation.deleteEdge = false;
}
} else {
options.manipulation.deleteEdge = x.options.manipulation.deleteEdge;
}
if(x.options.manipulation.addEdge === undefined){
options.manipulation.addEdge = function(data, callback) {
if (data.from == data.to) {
var r = confirm("Do you want to connect the node to itself?");
if (r === true) {
saveEdge(data, callback, "addEdge");
}
}
else {
saveEdge(data, callback, "addEdge");
}
};
} else if(typeof(x.options.manipulation.addEdge) === typeof(true)){
if(x.options.manipulation.addEdge){
options.manipulation.addEdge = function(data, callback) {
if (data.from == data.to) {
var r = confirm("Do you want to connect the node to itself?");
if (r === true) {
saveEdge(data, callback, "addEdge");
}
}
else {
saveEdge(data, callback, "addEdge");
}
};
} else {
options.manipulation.addEdge = false;
}
} else {
options.manipulation.addEdge = x.options.manipulation.addEdge;
}
if(x.options.manipulation.editEdge === undefined){
if(x.opts_manipulation.tab_edit_edge){
options.manipulation.editEdge = {editWithoutDrag : function(data, callback) {
var edge_data = edges.get(data.id);
document.getElementById('editedge-operation').innerHTML = "Edit Edge";
for (var edgecol = 0; edgecol < x.opts_manipulation.editEdgeCols.length; edgecol++){
document.getElementById('editedge-' + x.opts_manipulation.editEdgeCols[edgecol]).value = edge_data[x.opts_manipulation.editEdgeCols[edgecol]];
}
document.getElementById('editedge-saveButton').onclick = saveEdge.bind(this, data, callback, "editEdgeCols");
document.getElementById('editedge-cancelButton').onclick = cancelEdit.bind(this,callback);
document.getElementById('editedge-popUp').style.display = 'block';
}
}
} else {
options.manipulation.editEdge = function(data, callback) {
if (data.from == data.to) {
var r = confirm("Do you want to connect the node to itself?");
if (r === true) {
saveEdge(data, callback, "editEdge");
}
}
else {
saveEdge(data, callback, "editEdge");
}
};
}
} else if(typeof(x.options.manipulation.editEdge) === typeof(true)){
if(x.options.manipulation.editEdge){
if(x.opts_manipulation.tab_edit_edge){
options.manipulation.editEdge = {editWithoutDrag : function(data, callback) {
var edge_data = edges.get(data.id);
document.getElementById('editedge-operation').innerHTML = "Edit Edge";
for (var edgecol = 0; edgecol < x.opts_manipulation.editEdgeCols.length; edgecol++){
document.getElementById('editedge-' + x.opts_manipulation.editEdgeCols[edgecol]).value = edge_data[x.opts_manipulation.editEdgeCols[edgecol]];
}
document.getElementById('editedge-saveButton').onclick = saveEdge.bind(this, data, callback, "editEdgeCols");
document.getElementById('editedge-cancelButton').onclick = cancelEdit.bind(this,callback);
document.getElementById('editedge-popUp').style.display = 'block';
}
}
} else {
options.manipulation.editEdge = function(data, callback) {
if (data.from == data.to) {
var r = confirm("Do you want to connect the node to itself?");
if (r === true) {
saveEdge(data, callback, "editEdge");
}
}
else {
saveEdge(data, callback, "editEdge");
}
};
}
} else {
options.manipulation.editEdge = false;
}
} else {
options.manipulation.editEdge = x.options.manipulation.editEdge;
}
}
// create network
instance.network = new vis.Network(document.getElementById("graph"+el.id), data, options);
if (window.Shiny){
Shiny.onInputChange(el.id + '_initialized', true);
}
//*************************
//add values to idselection
//*************************
if(el_id.idselection){
var selectList = document.getElementById("nodeSelect"+el.id)
setNodeIdList(selectList, x.idselection, nodes)
if (window.Shiny){
changeInput('selected', document.getElementById("nodeSelect"+el.id).value);
}
}
//console.info(instance.network)
//save data for re-use and update
document.getElementById("graph"+el.id).chart = instance.network;
document.getElementById("graph"+el.id).options = options;
/////////
// popup
/////////
// Temporary variables to hold mouse x-y pos.s
var tempX = 0
var tempY = 0
// Main function to retrieve mouse x-y pos.s
function getMouseXY(e) {
tempX = e.clientX
tempY = e.clientY
// catch possible negative values in NS
if (tempX < 0){tempX = 0}
if (tempY < 0){tempY = 0}
}
document.addEventListener('mousemove', getMouseXY);
//this.body.emitter.emit("showPopup",{id:this.popupObj.id,x:t.x+3,y:t.y-5}))
// popup for title
var popupState = false;
var popupTimeout = null;
var vispopup = document.createElement("div");
// disable vis.js tooltip
var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = 'div.vis-tooltip {display : none}';
document.getElementsByTagName('head')[0].appendChild(style);
var popupStyle = 'position: fixed;visibility:hidden;padding: 5px;font-family: verdana;font-size:14px;font-color:#000000;background-color: #f5f4ed;-moz-border-radius: 3px;-webkit-border-radius: 3px;border-radius: 3px;border: 1px solid #808074;box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.2);max-width:400px;word-break: break-all'
if(x.tooltipStyle !== undefined){
popupStyle = x.tooltipStyle
}
var popupStay = 300;
if(x.tooltipStay !== undefined){
popupStay = x.tooltipStay
}
vispopup.setAttribute('style', popupStyle)
document.getElementById("graph"+el.id).appendChild(vispopup);
// add some event listeners to avoid it disappearing when the mouse if over it.
vispopup.addEventListener('mouseover',function () {
if (popupTimeout !== null) {
clearTimeout(popupTimeout);
popupTimeout = null;
}
});
// set the timeout when the mouse leaves it.
vispopup.addEventListener('mouseout',function () {
if (popupTimeout === null) {
myHidePopup(100);
}
});
// use the popup event to show
instance.network.on("showPopup", function(params) {
popupState = true;
myShowPopup(params);
})
// use the hide event to hide it
instance.network.on("hidePopup", function(params) {
// avoid double firing of this event, bug in 4.2.0
if (popupState === true) {
popupState = false;
myHidePopup(popupStay);
}
})
// hiding the popup through css and a timeout
function myHidePopup(delay) {
popupTimeout = setTimeout(function() {vispopup.style.visibility = 'hidden';}, delay);
}
// showing the popup
function myShowPopup(id) {
// get the data from the vis.DataSet
var nodeData = nodes.get([id]);
var edgeData = edges.get([id]);
// a node ?
if(nodeData[0] !== null && nodeData[0] !== undefined){
vispopup.innerHTML = nodeData[0].title;
// show and place the tooltip.
vispopup.style.visibility = 'visible';
vispopup.style.top = tempY - 20 + "px";
vispopup.style.left = tempX + 5 + "px";
} else if(edgeData[0] !== null && edgeData[0] !== undefined){
// so it's perhaps a edge ?
vispopup.innerHTML = edgeData[0].title;
// show and place the tooltip.
vispopup.style.visibility = 'visible';
vispopup.style.top = tempY - 20 + "px";
vispopup.style.left = tempX + 5 + "px";
} else {
// or a cluster ?
var node_cluster = instance.network.body.nodes[id]
if(node_cluster !== undefined){
vispopup.innerHTML = node_cluster.options.title;
// show and place the tooltip.
vispopup.style.visibility = 'visible';
vispopup.style.top = tempY - 20 + "px";
vispopup.style.left = tempX + 5 + "px";
}
}
// for sparkline. Eval script...
var any_script= vispopup.getElementsByTagName('script')
for (var n = 0; n < any_script.length; n++){
if(any_script[n].getAttribute("type") === "text/javascript"){
eval(any_script[n].innerHTML);
}
}
}
//*************************
// Events
//*************************
if(x.events !== undefined){
for (var key in x.events) {
instance.network.on(key, x.events[key]);
}
}
if(x.OnceEvents !== undefined){
for (var key in x.OnceEvents) {
instance.network.once(key, x.OnceEvents[key]);
}
}
if(x.ResetEvents !== undefined){
for (var key in x.ResetEvents) {
instance.network.off(key);
}
}
//*************************
// Selected Highlight
//*************************
function selectedHighlight(value) {
// get current nodes
var allNodes = nodes.get({returnType:"Object"});
// first resetEdges
resetAllEdges(edges, el_id.byselectionColor, el_id.highlightColor, instance.network);
var connectedNodes = [];
// get variable
var sel = el_id.byselection_variable;
// need to make an update?
var update = !(el_id.selectActive === false && value === "");
if (value !== "") {
var updateArray = [];
el_id.selectActive = true;
// mark all nodes as hard to read.
for (var nodeId in allNodes) {
var value_in = false;
// unique selection
if(el_id.byselection_multiple === false){
if(sel == "label"){
value_in = ((allNodes[nodeId]["label"] + "") === value) || ((allNodes[nodeId]["hiddenLabel"] + "") === value);
}else if(sel == "color"){
value_in = ((allNodes[nodeId]["color"] + "") === value) || ((allNodes[nodeId]["hiddenColor"] + "") === value);
}else {
value_in = (allNodes[nodeId][sel] + "") === value;
}
}else{ // multiple selection
if(sel == "label"){
var current_value = allNodes[nodeId]["label"] + "";
var value_split = current_value.split(",").map(Function.prototype.call, String.prototype.trim);
var current_value2 = allNodes[nodeId]["hiddenLabel"] + "";
var value_split2 = current_value.split(",").map(Function.prototype.call, String.prototype.trim);
value_in = (value_split.indexOf(value) !== -1) || (value_split2.indexOf(value) !== -1);
}else if(sel == "color"){
var current_value = allNodes[nodeId]["color"] + "";
var value_split = current_value.split(",").map(Function.prototype.call, String.prototype.trim);
var current_value2 = allNodes[nodeId]["hiddenColor"] + "";
var value_split2 = current_value.split(",").map(Function.prototype.call, String.prototype.trim);
value_in = (value_split.indexOf(value) !== -1) || (value_split2.indexOf(value) !== -1);
}else {
var current_value = allNodes[nodeId][sel] + "";
var value_split = current_value.split(",").map(Function.prototype.call, String.prototype.trim);
value_in = value_split.indexOf(value) !== -1;
}
}
if(value_in === false){ // not in selection, so as hard to read
nodeAsHardToRead(allNodes[nodeId], options, el_id.byselectionColor, el_id.highlightColor, instance.network, "node");
} else { // in selection, so reset if needed
connectedNodes = connectedNodes.concat(allNodes[nodeId].id);
resetOneNode(allNodes[nodeId], options, instance.network);
}
allNodes[nodeId].x = undefined;
allNodes[nodeId].y = undefined;
// update data
if (allNodes.hasOwnProperty(nodeId) && update) {
updateArray.push(allNodes[nodeId]);
}
}
if(update){
// set some edges as hard to read
var edgesHardToRead = edges.get({
fields: ['id', 'color', 'hiddenColor', 'hiddenLabel', 'label'],
filter: function (item) {
return (indexOf.call(connectedNodes, item.from, true) === -1) ;
},
returnType :'Array'
});
// all in degree nodes get their own color and their label back
for (i = 0; i < edgesHardToRead.length; i++) {
edgeAsHardToRead(edgesHardToRead[i], el_id.byselectionColor, el_id.highlightColor, instance.network, type = "edge")
}
edges.update(edgesHardToRead);
nodes.update(updateArray);
// select for highlight
if(el_id.highlight && x.nodes && el_id.byselectionHighlight){
neighbourhoodHighlight(connectedNodes, "click", el_id.highlightAlgorithm, false);
instance.network.selectNodes(connectedNodes)
}
}
}
else if (el_id.selectActive === true) {
//reset nodes
resetAllNodes(nodes, update, options, instance.network, false)
el_id.selectActive = false
}
}
//*************************
//Highlight
//*************************
var is_hovered = false;
var is_clicked = false;
function neighbourhoodHighlight(params, action_type, algorithm, reset_selectedBy) {
var nodes_in_clusters = instance.network.body.modules.clustering.clusteredNodes;
var have_cluster_nodes = false;
if(Object.keys(nodes_in_clusters).length > 0){
have_cluster_nodes = true;
nodes_in_clusters = Object.keys(nodes_in_clusters);
edges_in_clusters = Object.keys(instance.network.body.modules.clustering.clusteredEdges);
} else {
nodes_in_clusters = [];
edges_in_clusters = [];
}
var selectNode;
// get nodes data
var allNodes = nodes.get({returnType:"Object"});
// cluster
var array_cluster_id;
// update
var update = !(el_id.highlightActive === false && params.length === 0) | (el_id.selectActive === true && params.length === 0);
if(!(action_type == "hover" && is_clicked)){
// first resetEdges
resetAllEdges(edges, el_id.highlightColor, el_id.byselectionColor, instance.network);
if (params.length > 0) {
var is_cluster = instance.network.isCluster(params[0]);
var selectedNode;
if(is_cluster){
selectedNode = instance.network.getNodesInCluster(params[0]);
} else {
selectedNode = params;
}
var updateArray = [];
if(el_id.idselection){
selectNode = document.getElementById('nodeSelect'+el.id);
if(is_cluster === false){
if(x.idselection.values !== undefined){
if(indexOf.call(x.idselection.values, selectedNode[0], true) > -1){
selectNode.value = selectedNode[0];
}else{
selectNode.value = "";
}
}else{
selectNode.value = selectedNode[0];
}
if (window.Shiny){
changeInput('selected', selectNode.value);
}
}
}
el_id.highlightActive = true;
var i,j;
var degrees = el_id.degree;
// mark all nodes as hard to read.
for (var nodeId in instance.network.body.nodes) {
if(instance.network.isCluster(nodeId)){
nodeAsHardToRead(instance.network.body.nodes[nodeId], options, el_id.highlightColor, el_id.byselectionColor, instance.network, "cluster");
}else {
var tmp_node = allNodes[nodeId];
if(tmp_node !== undefined){
nodeAsHardToRead(tmp_node, options, el_id.highlightColor, el_id.byselectionColor, instance.network, "node");
tmp_node.x = undefined;
tmp_node.y = undefined;
}
}
}
if(algorithm === "all"){
var connectedNodes;
if(degrees > 0){
connectedNodes = [];
for (j = 0; j < selectedNode.length; j++) {
connectedNodes = connectedNodes.concat(instance.network.getConnectedNodes(selectedNode[j], true));
}
connectedNodes = uniqueArray(connectedNodes, true, instance.network);
}else{
connectedNodes = selectedNode;
}
var allConnectedNodes = [];
// get the nodes to color
if(degrees >= 2){
for (i = 2; i <= degrees; i++) {
var previous_connectedNodes = connectedNodes;
var currentlength = connectedNodes.length;
for (j = 0; j < currentlength; j++) {
connectedNodes = uniqueArray(connectedNodes.concat(instance.network.getConnectedNodes(connectedNodes[j])), true, instance.network);
}
if (connectedNodes.length === previous_connectedNodes.length) { break; }
}
}
// nodes to just label
for (j = 0; j < connectedNodes.length; j++) {
allConnectedNodes = allConnectedNodes.concat(instance.network.getConnectedNodes(connectedNodes[j]));
}
allConnectedNodes = uniqueArray(allConnectedNodes, true, instance.network);
if(el_id.highlightLabelOnly === true){
// all higher degree nodes get a different color and their label back
array_cluster_id = [];
for (i = 0; i < allConnectedNodes.length; i++) {
if (allNodes[allConnectedNodes[i]].hiddenLabel !== undefined) {
allNodes[allConnectedNodes[i]].label = allNodes[allConnectedNodes[i]].hiddenLabel;
allNodes[allConnectedNodes[i]].hiddenLabel = undefined;
if(have_cluster_nodes){
if(indexOf.call(nodes_in_clusters, allConnectedNodes[i], true) > -1){
array_cluster_id = array_cluster_id.concat(instance.network.clustering.findNode(allConnectedNodes[i])[0]);
}
}
}
}
if(array_cluster_id.length > 0){
array_cluster_id = uniqueArray(array_cluster_id, false, instance.network);
for (i = 0; i < array_cluster_id.length; i++) {
instance.network.body.nodes[array_cluster_id[i]].setOptions({label : instance.network.body.nodes[array_cluster_id[i]].options.hiddenLabel, hiddenLabel:undefined})
}
}
}
// all in degree nodes get their own color and their label back + main nodes
connectedNodes = connectedNodes.concat(selectedNode);
if (window.Shiny){
Shiny.onInputChange(el.id + '_highlight_color_id', uniqueShiny(connectedNodes));
}
if(el_id.highlightLabelOnly === true){
if (window.Shiny){
Shiny.onInputChange(el.id + '_highlight_label_id', allConnectedNodes.filter(function(x){ return !connectedNodes.includes(x)}));
}
}
array_cluster_id = [];
for (i = 0; i < connectedNodes.length; i++) {
resetOneNode(allNodes[connectedNodes[i]], options, instance.network);
if(have_cluster_nodes){
if(indexOf.call(nodes_in_clusters, connectedNodes[i], true) > -1){
array_cluster_id = array_cluster_id.concat(instance.network.clustering.findNode(connectedNodes[i])[0]);
}
}
}
if(array_cluster_id.length > 0){
array_cluster_id = uniqueArray(array_cluster_id, false, instance.network);
for (i = 0; i < array_cluster_id.length; i++) {
resetOneCluster(instance.network.body.nodes[array_cluster_id[i]], options, instance.network);
}
}
// set some edges as hard to read
var edgesHardToRead = edges.get({
fields: ['id', 'color', 'hiddenColor', 'hiddenLabel', 'label'],
filter: function (item) {
return ((indexOf.call(connectedNodes, item.from, true) === -1)) ;
},
returnType :'Array'
});
// all in degree nodes get their own color and their label back
array_cluster_id = [];
var tmp_cluster_id;
for (i = 0; i < edgesHardToRead.length; i++) {
edgeAsHardToRead(edgesHardToRead[i], el_id.highlightColor, el_id.byselectionColor, instance.network, type = "edge")
if(have_cluster_nodes){
if(indexOf.call(edges_in_clusters, edgesHardToRead[i].id, true) > -1){
tmp_cluster_id = instance.network.clustering.getClusteredEdges(edgesHardToRead[i].id);
if(tmp_cluster_id.length > 1){
array_cluster_id = array_cluster_id.concat(tmp_cluster_id[0]);
}
}
}
}
if(array_cluster_id.length > 0){
array_cluster_id = uniqueArray(array_cluster_id, false, instance.network);
for (i = 0; i < array_cluster_id.length; i++) {
edgeAsHardToRead(instance.network.body.edges[array_cluster_id[i]].options, el_id.highlightColor, el_id.byselectionColor, instance.network, type = "cluster")
}
}
edges.update(edgesHardToRead);
} else if(algorithm === "hierarchical"){
var degree_from = degrees.from;
var degree_to = degrees.to;
degrees = Math.max(degree_from, degree_to);
var allConnectedNodes = [];
var currentConnectedFromNodes = [];
var currentConnectedToNodes = [];
var connectedFromNodes = [];
var connectedToNodes = [];
if(degree_from > 0){
connectedFromNodes = edges.get({
fields: ['from'],
filter: function (item) {
return ((indexOf.call(selectedNode, item.to, true) !== -1)) ;
},
returnType :'Array'
});
}
if(degree_to > 0){
connectedToNodes = edges.get({
fields: ['to'],
filter: function (item) {
return ((indexOf.call(selectedNode, item.from, true) !== -1)) ;
},
returnType :'Array'
});
}
for (j = 0; j < connectedFromNodes.length; j++) {
allConnectedNodes = allConnectedNodes.concat(connectedFromNodes[j].from);
currentConnectedFromNodes = currentConnectedFromNodes.concat(connectedFromNodes[j].from);
}
for (j = 0; j < connectedToNodes.length; j++) {
allConnectedNodes = allConnectedNodes.concat(connectedToNodes[j].to);
currentConnectedToNodes = currentConnectedToNodes.concat(connectedToNodes[j].to);
}
var go_from;
var go_to;
if(degrees > 1){
for (i = 2; i <= degrees; i++) {
go_from = false;
go_to = false;
if(currentConnectedFromNodes.length > 0 && i <= degree_from){
connectedFromNodes = edges.get({
fields: ['from'],
filter: function (item) {
return indexOf.call(currentConnectedFromNodes, item.to, true) > -1;
},
returnType :'Array'
});
go_from = true;
}
if(currentConnectedToNodes.length > 0 && i <= degree_to){
connectedToNodes = edges.get({
fields: ['to'],
filter: function (item) {
return indexOf.call(currentConnectedToNodes, item.from, true) > -1;
},
returnType :'Array'
});
go_to = true;
}
if(go_from === true){
currentConnectedFromNodes = [];
for (j = 0; j < connectedFromNodes.length; j++) {
allConnectedNodes = allConnectedNodes.concat(connectedFromNodes[j].from);
currentConnectedFromNodes = currentConnectedFromNodes.concat(connectedFromNodes[j].from);
}
}
if(go_to === true){
currentConnectedToNodes = [];
for (j = 0; j < connectedToNodes.length; j++) {
allConnectedNodes = allConnectedNodes.concat(connectedToNodes[j].to);
currentConnectedToNodes = currentConnectedToNodes.concat(connectedToNodes[j].to);
}
}
if (go_from === false && go_to === false) { break;}
}
}
allConnectedNodes = uniqueArray(allConnectedNodes, true, instance.network).concat(selectedNode);
var nodesWithLabel = [];
if(el_id.highlightLabelOnly === true){
if(degrees > 0){
// nodes to just label
for (j = 0; j < currentConnectedToNodes.length; j++) {
nodesWithLabel = nodesWithLabel.concat(instance.network.getConnectedNodes(currentConnectedToNodes[j]));
}
for (j = 0; j < currentConnectedFromNodes.length; j++) {
nodesWithLabel = nodesWithLabel.concat(instance.network.getConnectedNodes(currentConnectedFromNodes[j]));
}
nodesWithLabel = uniqueArray(nodesWithLabel, true, instance.network);
} else{
nodesWithLabel = currentConnectedToNodes;
nodesWithLabel = nodesWithLabel.concat(currentConnectedFromNodes);
nodesWithLabel = uniqueArray(nodesWithLabel, true, instance.network);
}
}
// all higher degree nodes get a different color and their label back
array_cluster_id = [];
for (i = 0; i < nodesWithLabel.length; i++) {
if (allNodes[nodesWithLabel[i]].hiddenLabel !== undefined) {
allNodes[nodesWithLabel[i]].label = allNodes[nodesWithLabel[i]].hiddenLabel;
allNodes[nodesWithLabel[i]].hiddenLabel = undefined;
if(have_cluster_nodes){
if(indexOf.call(nodes_in_clusters, nodesWithLabel[i], true) > -1){
array_cluster_id = array_cluster_id.concat(instance.network.clustering.findNode(nodesWithLabel[i])[0]);
}
}
}
}
if(array_cluster_id.length > 0){
array_cluster_id = uniqueArray(array_cluster_id, false, instance.network);
for (i = 0; i < array_cluster_id.length; i++) {
instance.network.body.nodes[array_cluster_id[i]].setOptions({label : instance.network.body.nodes[array_cluster_id[i]].options.hiddenLabel, hiddenLabel:undefined})
}
}
// all in degree nodes get their own color and their label back
array_cluster_id = [];
for (i = 0; i < allConnectedNodes.length; i++) {
resetOneNode(allNodes[allConnectedNodes[i]], options, instance.network);
if(have_cluster_nodes){
if(indexOf.call(nodes_in_clusters, allConnectedNodes[i], true) > -1){
array_cluster_id = array_cluster_id.concat(instance.network.clustering.findNode(allConnectedNodes[i])[0]);
}
}
}
if(array_cluster_id.length > 0){
array_cluster_id = uniqueArray(array_cluster_id, false, instance.network);
for (i = 0; i < array_cluster_id.length; i++) {
resetOneCluster(instance.network.body.nodes[array_cluster_id[i]], options, instance.network);
}
}
if (window.Shiny){
Shiny.onInputChange(el.id + '_highlight_color_id', uniqueShiny(allConnectedNodes));
}
if(el_id.highlightLabelOnly === true){
if (window.Shiny){
Shiny.onInputChange(el.id + '_highlight_label_id', nodesWithLabel.filter(function(x) {return !allConnectedNodes.includes(x)}));
}
}
// set some edges as hard to read
var edgesHardToRead = edges.get({
fields: ['id', 'color', 'hiddenColor', 'hiddenLabel', 'label'],
filter: function (item) {
return ((indexOf.call(allConnectedNodes, item.from, true) === -1) || (indexOf.call(allConnectedNodes, item.to, true) === -1)) ;
},
returnType :'Array'
});
array_cluster_id = [];
for (i = 0; i < edgesHardToRead.length; i++) {
edgeAsHardToRead(edgesHardToRead[i], el_id.highlightColor, el_id.byselectionColor, instance.network, type = "edge")
if(have_cluster_nodes){
if(indexOf.call(edges_in_clusters, edgesHardToRead[i].id, true) > -1){
var tmp_cluster_id = instance.network.clustering.getClusteredEdges(edgesHardToRead[i].id);
if(tmp_cluster_id.length > 1){
array_cluster_id = array_cluster_id.concat(tmp_cluster_id[0]);
}
}
}
}
if(array_cluster_id.length > 0){
array_cluster_id = uniqueArray(array_cluster_id, false, instance.network);
for (i = 0; i < array_cluster_id.length; i++) {
edgeAsHardToRead(instance.network.body.edges[array_cluster_id[i]].options, el_id.highlightColor, el_id.byselectionColor, instance.network, type = "cluster");
}
}
edges.update(edgesHardToRead);
}
if(update){
if(!(action_type == "hover")){
is_clicked = true;
}
// transform the object into an array
var updateArray = [];
for (nodeId in allNodes) {
if (allNodes.hasOwnProperty(nodeId)) {
updateArray.push(allNodes[nodeId]);
}
}
nodes.update(updateArray);
}else{
is_clicked = false;
}
}
else if (el_id.highlightActive === true | el_id.selectActive === true) {
// reset nodeSelect list if actived
if(el_id.idselection){
resetList("nodeSelect", el.id, 'selected');
}
//reset nodes
resetAllNodes(nodes, update, options, instance.network, false)
el_id.highlightActive = false;
is_clicked = false;
if (window.Shiny){
Shiny.onInputChange(el.id + '_highlight_label_id', null)
Shiny.onInputChange(el.id + '_highlight_color_id', null)
}
}
}
// reset selectedBy list if actived
if(el_id.byselection && reset_selectedBy){
resetList("selectedBy", el.id, 'selectedBy');
}
}
function onClickIDSelection(selectedItems) {
var selectNode;
if(el_id.idselection){
if (selectedItems.nodes.length !== 0) {
selectNode = document.getElementById('nodeSelect'+el.id);
if(x.idselection.values !== undefined){
if(indexOf.call(x.idselection.values, selectedItems.nodes[0], true) > -1){
selectNode.value = selectedItems.nodes;
}else{
selectNode.value = "";
}
}else{
selectNode.value = selectedItems.nodes;
}
if (window.Shiny){
changeInput('selected', selectNode.value);
}
}else{
resetList("nodeSelect", el.id, 'selected');
}
}
if(el_id.byselection){
// reset selectedBy list if actived
if (selectedItems.nodes.length === 0) {
resetList("selectedBy", el.id, 'selectedBy');
selectedHighlight("");
}
}
}
// shared click function (selectedNodes)
document.getElementById("graph"+el.id).myclick = function(params){
if(el_id.highlight && x.nodes){
neighbourhoodHighlight(params.nodes, "click", el_id.highlightAlgorithm, true);
}else if((el_id.idselection || el_id.byselection) && x.nodes){
onClickIDSelection(params)
}
};
// Set event in relation with highlightNearest
instance.network.on("click", function(params){
if(el_id.highlight && x.nodes){
neighbourhoodHighlight(params.nodes, "click", el_id.highlightAlgorithm, true);
}else if((el_id.idselection || el_id.byselection) && x.nodes){
onClickIDSelection(params)
}
});
instance.network.on("hoverNode", function(params){
if(el_id.hoverNearest && x.nodes){
neighbourhoodHighlight([params.node], "hover", el_id.highlightAlgorithm, true);
}
});
instance.network.on("blurNode", function(params){
if(el_id.hoverNearest && x.nodes){
neighbourhoodHighlight([], "hover", el_id.highlightAlgorithm, true);
}
});
//*************************
//collapse
//*************************
instance.network.on("doubleClick", function(params){
if(el_id.collapse){
collapsedNetwork(params.nodes, el_id.collapseFit, el_id.collapseResetHighlight,
el_id.clusterOptions, el_id.collapseLabelSuffix,
el_id.tree, instance.network, el.id)
}
});
if(el_id.collapse){
instance.network.on("doubleClick", networkOpenCluster);
}
//*************************
//footer
//*************************
var div_footer = document.createElement('div');
div_footer.id = "footer"+el.id;
div_footer.setAttribute('style', 'font-family:Georgia, Times New Roman, Times, serif;font-size:12px;text-align:center;background-color: inherit;');
div_footer.style.display = 'none';
document.getElementById("graph" + el.id).appendChild(div_footer);
if(x.footer !== null){
div_footer.innerHTML = x.footer.text;
div_footer.setAttribute('style', x.footer.style + ';background-color: inherit;');
div_footer.style.display = 'block';
}
//*************************
// export
//*************************
if(x.export !== undefined){
var downloaddiv = document.createElement('div');
downloaddiv.setAttribute('style', 'float:right; width:100%;background-color: inherit;');
var downloadbutton = document.createElement("button");
downloadbutton.setAttribute('style', x.export.css);
downloadbutton.style.position = "relative";
downloadbutton.id = "download"+el.id;
downloadbutton.appendChild(document.createTextNode(x.export.label));
downloaddiv.appendChild(downloadbutton);
var hr = document.createElement("hr");
hr.setAttribute('style', 'height:5px; visibility:hidden; margin-bottom:-1px;');
downloaddiv.appendChild(hr);
document.getElementById("maindiv"+el.id).appendChild(downloaddiv);
document.getElementById("download"+el.id).onclick = function() {
// height control for export
var addHeightExport = document.getElementById("graph" + el.id).offsetHeight + idList.offsetHeight + byList.offsetHeight + downloaddiv.offsetHeight;
if(div_title.style.display !== 'none'){
addHeightExport = addHeightExport + div_title.offsetHeight;
}
if(div_subtitle.style.display !== 'none'){
addHeightExport = addHeightExport + div_subtitle.offsetHeight;
}
if(div_footer.style.display !== 'none'){
addHeightExport = addHeightExport + div_footer.offsetHeight;
} else {
addHeightExport = addHeightExport + 15;
}
downloadbutton.style.display = 'none';
var export_background = x.export.background;
if(x.background !== "transparent" && x.background !== "rgba(0, 0, 0, 0)"){
export_background = x.background
}
if(x.export.type !== "pdf"){
html2canvas(el_id, {
background: export_background,
height : addHeightExport,
onrendered: function(canvas) {
canvas.toBlobHD(function(blob) {
saveAs(blob, x.export.name);
}, "image/"+x.export.type);
}
});
} else {
html2canvas(el_id, {
background: export_background,
height : addHeightExport,
onrendered: function(canvas) {
var myImage = canvas.toDataURL("image/png", 1.0);
//var imgWidth = (canvas.width * 25.4) / 24;
//var imgHeight = (canvas.height * 25.4) / 24;
var table = new jsPDF('l', 'pt', [canvas.width, canvas.height]);
table.addImage(myImage, 'JPEG', 0, 0, canvas.width, canvas.height);
table.save(x.export.name);
}
});
}
downloadbutton.style.display = 'block';
};
}
//*************************
// dataManipulation
//*************************
function clearPopUp() {
if(x.opts_manipulation.tab_add_node){
document.getElementById('addnode-saveButton').onclick = null;
document.getElementById('addnode-cancelButton').onclick = null;
document.getElementById('addnode-popUp').style.display = 'none';
}
if(x.opts_manipulation.tab_edit_node){
document.getElementById('editnode-saveButton').onclick = null;
document.getElementById('editnode-cancelButton').onclick = null;
document.getElementById('editnode-popUp').style.display = 'none';
}
if(x.opts_manipulation.tab_edit_edge){
document.getElementById('editedge-saveButton').onclick = null;
document.getElementById('editedge-cancelButton').onclick = null;
document.getElementById('editedge-popUp').style.display = 'none';
}
}
function saveNode(data, callback, cmd) {
var iname;
var prediv;
if(cmd === "addNode"){
iname = "addNodeCols";
prediv = 'addnode-';
} else {
iname = "editNodeCols";
prediv = 'editnode-';
}
var obj = {id : data.id}
for (var nodecol = 0; nodecol < x.opts_manipulation[iname].length; nodecol++){
var add_node_val = document.getElementById(prediv + x.opts_manipulation[iname][nodecol]).value;
var add_node_type = document.getElementById(prediv + x.opts_manipulation[iname][nodecol]).type;
if(add_node_type && add_node_type === "number"){
add_node_val = parseFloat(add_node_val)
}
if(add_node_val !== "undefined"){
obj[x.opts_manipulation[iname][nodecol]] = add_node_val
}
}
var update_obj = clone(obj);
update_obj.x = data.x;
update_obj.y = data.y;
nodes.update(update_obj);
if (window.Shiny){
obj.cmd = cmd;
Shiny.onInputChange(el.id + '_graphChange', obj);
}
clearPopUp();
callback(null);
}
function saveEdge(data, callback, cmd) {
if(cmd === "editEdge"){
callback(data); //must be first called for egde id !
if (window.Shiny){
var obj = {cmd: cmd, id: data.id, from: data.from, to: data.to};
Shiny.onInputChange(el.id + '_graphChange', obj);
}
} else if(cmd === "addEdge"){
callback(data); //must be first called for egde id !
if (window.Shiny){
var obj = {cmd: cmd, id: data.id, from: data.from, to: data.to};
Shiny.onInputChange(el.id + '_graphChange', obj);
}
} else if(cmd === "editEdgeCols"){
for (var edgecol = 0; edgecol < x.opts_manipulation.editEdgeCols.length; edgecol++){
var add_edge_val = document.getElementById("editedge-" + x.opts_manipulation.editEdgeCols[edgecol]).value;
var add_edge_type = document.getElementById("editedge-" + x.opts_manipulation.editEdgeCols[edgecol]).type;
if(add_edge_type && add_edge_type === "number"){
add_edge_val = parseFloat(add_edge_val)
}
if(add_edge_val !== "undefined"){
data[x.opts_manipulation.editEdgeCols[edgecol]] = add_edge_val
}
}
if (window.Shiny){
var obj = {cmd: "editEdge", id : data.id}
for (var edgecol = 0; edgecol < x.opts_manipulation.editEdgeCols.length; edgecol++){
if(data[x.opts_manipulation.editEdgeCols[edgecol]] !== "undefined"){
obj[x.opts_manipulation.editEdgeCols[edgecol]] = data[x.opts_manipulation.editEdgeCols[edgecol]];
}
}
Shiny.onInputChange(el.id + '_graphChange', obj);
}
callback(data);
clearPopUp();
}
}
function deleteSubGraph(data, callback) {
if (window.Shiny){
var obj = {cmd: "deleteElements", nodes: data.nodes, edges: data.edges}
Shiny.onInputChange(el.id + '_graphChange', obj);
}
callback(data);
}
function cancelEdit(callback) {
clearPopUp();
callback(null);
}
//*************************
// CLUSTERING
//*************************
if(x.clusteringGroup || x.clusteringColor || x.clusteringHubsize || x.clusteringConnection){
var clusterbutton = document.createElement("input");
clusterbutton.id = "backbtn"+el.id;
clusterbutton.setAttribute('type', 'button');
clusterbutton.setAttribute('value', 'Reinitialize clustering');
clusterbutton.setAttribute('style', 'background-color:#FFFFFF;border: none');
el_id.appendChild(clusterbutton);
clusterbutton.onclick = function(){
// reset some parameters / data before
if (el_id.selectActive === true | el_id.highlightActive === true) {
//reset nodes
neighbourhoodHighlight([], "click", el_id.highlightAlgorithm, true);
if (el_id.selectActive === true){
el_id.selectActive = false;
resetList('selectedBy',el.id, 'selectedBy');
}
if (el_id.highlightActive === true){
el_id.highlightActive = false;
resetList('nodeSelect', el.id, 'selected');
}
}
//instance.network.setData(data);
if(x.clusteringColor){
clusterByColor();
}
if(x.clusteringGroup){
clusterByGroup(x.clusteringGroup.groups);
}
if(x.clusteringHubsize){
clusterByHubsize();
}
if(x.clusteringConnection){
clusterByConnection();
}
instance.network.fit();
}
}
if(x.clusteringGroup || x.clusteringColor || x.clusteringOutliers || x.clusteringHubsize || x.clusteringConnection){
// if we click on a node, we want to open it up!
instance.network.on("doubleClick", function (params){
if (params.nodes.length === 1) {
if (instance.network.isCluster(params.nodes[0]) === true) {
is_clicked = false;
instance.network.openCluster(params.nodes[0], {releaseFunction : function(clusterPosition, containedNodesPositions) {
return containedNodesPositions;
}});
// must be better...
resetAllEdges(edges, el_id.highlightColor, el_id.byselectionColor, instance.network);
resetAllNodes(nodes, true, options, instance.network, true);
} else {
if(x.clusteringGroup){
var array_group = nodes.get({
fields: ['group'],
filter: function (item) {
return item.id === params.nodes[0] ;
},
returnType :'Array'
});
clusterByGroup([array_group[0].group]);
}
}
}
});
}
//*************************
//clustering Connection
//*************************
if(x.clusteringConnection){
function clusterByConnection() {
for (var i = 0; i < x.clusteringConnection.nodes.length; i++) {
instance.network.clusterByConnection(x.clusteringConnection.nodes[i])
}
}
clusterByConnection();
}
//*************************
//clustering hubsize
//*************************
if(x.clusteringHubsize){
function clusterByHubsize() {
var clusterOptionsByData = {
processProperties: function(clusterOptions, childNodes) {
var cluster_level = 9999999
for (var i = 0; i < childNodes.length; i++) {
//totalMass += childNodes[i].mass;
if(childNodes[i].level){
cluster_level = Math.min(cluster_level, childNodes[i].level)
}
if(i === 0){
//clusterOptions.shape = childNodes[i].shape;
clusterOptions.color = childNodes[i].color.background;
}else{
//if(childNodes[i].shape !== clusterOptions.shape){
//clusterOptions.shape = 'database';
//}
if(childNodes[i].color.background !== clusterOptions.color){
clusterOptions.color = 'grey';
}
}
}
clusterOptions.label = "[" + childNodes.length + "]";
if(cluster_level !== 9999999){
clusterOptions.level = cluster_level
}
return clusterOptions;
},
clusterNodeProperties: {borderWidth:3, shape:'box', font:{size:30}}
}
if(x.clusteringHubsize.size > 0){
instance.network.clusterByHubsize(x.clusteringHubsize.size, clusterOptionsByData);
}else{
instance.network.clusterByHubsize(undefined, clusterOptionsByData);
}
}
clusterByHubsize();
}
if(x.clusteringColor){
//*************************
//clustering color
//*************************
function clusterByColor() {
var colors = x.clusteringColor.colors
var clusterOptionsByData;
for (var i = 0; i < colors.length; i++) {
var color = colors[i];
var sh = x.clusteringColor.shape[i];
var force = x.clusteringColor.force[i];
clusterOptionsByData = {
joinCondition: function (childOptions) {
return childOptions.color.background == color; // the color is fully defined in the node.
},
processProperties: function (clusterOptions, childNodes, childEdges) {
var totalMass = 0;
var cluster_level = 9999999;
for (var i = 0; i < childNodes.length; i++) {
totalMass += childNodes[i].mass;
if(childNodes[i].level){
cluster_level = Math.min(cluster_level, childNodes[i].level)
}
if(force === false){
if(i === 0){
clusterOptions.shape = childNodes[i].shape;
}else{
if(childNodes[i].shape !== clusterOptions.shape){
clusterOptions.shape = sh;
}
}
} else {
clusterOptions.shape = sh;
}
}
clusterOptions.value = totalMass;
if(cluster_level !== 9999999){
clusterOptions.level = cluster_level
}
return clusterOptions;
},
clusterNodeProperties: {id: 'cluster:' + color, borderWidth: 3, color:color, label: x.clusteringColor.label + color}
}
instance.network.cluster(clusterOptionsByData);
}
}
clusterByColor();
}
//*************************
//clustering groups
//*************************
if(x.clusteringGroup){
function clusterByGroup(groups) {
var clusterOptionsByData;
for (var i = 0; i < groups.length; i++) {
var group = groups[i];
var j = x.clusteringGroup.groups.indexOf(group);
if(j !== -1) {
var col = x.clusteringGroup.color[j];
var sh = x.clusteringGroup.shape[j];
var force = x.clusteringGroup.force[j];
var sc_size = x.clusteringGroup.scale_size[j];
clusterOptionsByData = {
joinCondition: function (childOptions) {
return childOptions.group == group; //
},
processProperties: function (clusterOptions, childNodes, childEdges) {
var totalMass = 0;
var cluster_level = 9999999;
for (var i = 0; i < childNodes.length; i++) {
totalMass += childNodes[i].mass;
if(childNodes[i].level){
cluster_level = Math.min(cluster_level, childNodes[i].level)
}
if(force === false){
if(i === 0){
clusterOptions.shape = childNodes[i].shape;
clusterOptions.color = childNodes[i].color.background;
}else{
if(childNodes[i].shape !== clusterOptions.shape){
clusterOptions.shape = sh;
}
if(childNodes[i].color.background !== clusterOptions.color){
clusterOptions.color = col;
}
}
} else {
clusterOptions.shape = sh;
clusterOptions.color = col;
}
}
if(sc_size){
clusterOptions.value = totalMass;
}
if(cluster_level !== 9999999){
clusterOptions.level = cluster_level
}
return clusterOptions;
},
clusterNodeProperties: {id: 'cluster:' + group, borderWidth: 3, label:x.clusteringGroup.label + group}
}
instance.network.cluster(clusterOptionsByData);
}
}
}
clusterByGroup(x.clusteringGroup.groups);
}
//*************************
//clustering by zoom
//*************************
if(x.clusteringOutliers){
clusterFactor = x.clusteringOutliers.clusterFactor;
// set the first initial zoom level
instance.network.on('initRedraw', function() {
if (lastClusterZoomLevel === 0) {
lastClusterZoomLevel = instance.network.getScale();
}
});
// we use the zoom event for our clustering
instance.network.on('zoom', function (params) {
if(ctrlwait === 0){
if (params.direction == '-') {
if (params.scale < lastClusterZoomLevel*clusterFactor) {
makeClusters(params.scale);
lastClusterZoomLevel = params.scale;
}
}
else {
openClusters(params.scale);
}
}
});
}
// make the clusters
function makeClusters(scale) {
ctrlwait = 1;
var clusterOptionsByData = {
processProperties: function (clusterOptions, childNodes) {
clusterIndex = clusterIndex + 1;
var childrenCount = 0;
var cluster_level = 9999999;
for (var i = 0; i < childNodes.length; i++) {
childrenCount += childNodes[i].childrenCount || 1;
if(childNodes[i].level){
cluster_level = Math.min(cluster_level, childNodes[i].level)
}
}
clusterOptions.childrenCount = childrenCount;
clusterOptions.label = "# " + childrenCount + "";
clusterOptions.font = {size: childrenCount*5+30}
clusterOptions.id = 'cluster:' + clusterIndex;
clusters.push({id:'cluster:' + clusterIndex, scale:scale});
if(cluster_level !== 9999999){
clusterOptions.level = cluster_level
}
return clusterOptions;
},
clusterNodeProperties: {borderWidth: 3, shape: 'database', font: {size: 30}}
}
instance.network.clusterOutliers(clusterOptionsByData);
if (x.clusteringOutliers.stabilize) {
instance.network.stabilize();
};
ctrlwait = 0;
}
// open them back up!
function openClusters(scale) {
ctrlwait = 1;
var newClusters = [];
var declustered = false;
for (var i = 0; i < clusters.length; i++) {
if (clusters[i].scale < scale) {
instance.network.openCluster(clusters[i].id);
lastClusterZoomLevel = scale;
declustered = true;
}
else {
newClusters.push(clusters[i])
}
}
clusters = newClusters;
if (x.clusteringOutliers.stabilize) {
instance.network.stabilize();
};
ctrlwait = 0;
}
//******************
// init selection
//******************
if(el_id.idselection && x.nodes && x.idselection.selected !== undefined){
onIdChange(''+ x.idselection.selected, true);
}
if(el_id.byselection && x.nodes && x.byselection.selected !== undefined){
onByChange(x.byselection.selected);
selectNode = document.getElementById('selectedBy'+el.id);
selectNode.value = x.byselection.selected;
}
// try to fix icons loading css bug...
function iconsRedraw() {
setTimeout(function(){
if(instance.network)
instance.network.redraw();
if(instance.legend)
instance.legend.redraw();
}, 250);
};
if(x.iconsRedraw !== undefined){
if(x.iconsRedraw){
iconsRedraw();
instance.network.once("stabilized", function(){iconsRedraw();})
}
}
},
resize: function(el, width, height, instance) {
if(instance.network)
instance.network.fit();
if(instance.legend)
instance.legend.fit();
}
});